链接:CodeForces -1208F Bits And Pieces
题意:
给出一个长度为 的序列 ,求 的最大值。
分析:
假设固定一个 ,对于为 的二进制位,或运算一定得 ,不用考虑;但对于为 的二进制位,只有和 或运算才能使得结果值更大,也就是说要 和 的对应二进制位为 (因为 和 之间是与运算)。
例如当 (假设 ),则要令值最大应当尽可能找到 和 ,其子集均包括 。
①dp过程
这里就要用到SoS DP的思想:https://codeforces.com/blog/entry/45223
:序列中 子集含有 的 的位置集合,由于 ,所以只要贪心地 保留位置最靠右(最大)的两个位置 即可。
所以可以先将所有 放入 数组当中,对于 ,其 子集有 个(其中 为 二进制位为 的个数),若对于每一个 ,都找到所有子集并更新,时间复杂度会达到 。
由于很多 都有相同的子集,所以多了很多重复更新。我们可以考虑,一个数,其子集的子集,仍然是其子集,所以可以 每次仅更新将 其中一位二进制位从 变为 的子集(之后这些子集再去更新下一层子集)。例如 ,则仅更新 。
由于子集肯定小于原数,那么可以从最大的 开始,从后往前更新 ~ 的所有数,时间复杂度为 。
②求解答案
枚举 ,根据 的二进制位 贪心地构造一个值 ,保证 的两个最大位置均 ,最大的 即为答案。
以下代码:
#include<bits/stdc++.h>
#define LL long long
#define PII pair<int,int>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e6+50;
const int maxa=2e6+50;
int n,a[maxn];
PII dp[maxa];
void updata(int x,int pos)
{
if(dp[x].first<pos)
{
dp[x].second=dp[x].first;
dp[x].first=pos;
}
else if(dp[x].second<pos&&pos!=dp[x].first) //注意first和second的位置不能相同
dp[x].second=pos;
}
void get_dp()
{
for(int x=maxa-1;x>=0;x--) //从大到小更新
{
for(int i=20;i>=0;i--)
{
if(((x>>i)&1)==1) //枚举x为1的二进制位
{
updata(x^(1<<i),dp[x].first); //更新 把x的一个为1的二进制位变为0的子集
updata(x^(1<<i),dp[x].second);
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
updata(a[i],i);
}
get_dp();
int ans=0;
for(int i=1;i<=n-2;i++)
{
int w=0;
for(int j=20;j>=0;j--)
{ //找到a[i]为0的二进制位
if(((a[i]>>j)&1)==0&&dp[w|(1<<j)].first>i&&dp[w|(1<<j)].second>i)
w=w|(1<<j);
}
ans=max(ans,a[i]|w);
}
printf("%d",ans);
return 0;
}