题意:
给你一些数,让你求 a i ∣ ( a j & a k ) a_i|(a_j\&a_k) ai∣(aj&ak)的最大值,i<j<k
题解:
这里有一篇可看的博客:This way
SOSDP求的是
也就是mask集合的所有子集的值的和
dp[s][j]就表示与集合s前i位不同的所有s的子集的前缀和。
那么这道题从后往前做,也就是枚举
a
i
a_i
ai,然后查看对于每一位是否存在,那么在这题里dp就表示状态为s的情况有几个,并且我们只需要两个,那么只需要记录到dp[s][i]是大于1的时候即可。
我们从大到小枚举,s表示的是当前集合是什么,因为在
a
i
&
(
1
<
<
j
)
a_i\&(1<<j)
ai&(1<<j)为1的时候可以直接取
a
i
a_i
ai的这一位,但是如果是0的时候我们就需要尽可能的将
a
j
a_j
aj和
a
k
a_k
ak的这一位置为1,那么s接下来这一位必须是1,这样看它就像棵字典树来着。
并且对于每一个ai,我们将他的所有子集加入到DP中,所以即使是1001状态,也能访问到1111状态
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5;
int dp[(1<<21)][21];
void add(int x,int k)
{
if(k>20)
return ;
if(dp[x][k]>1)
return ;
dp[x][k]++;
add(x,k+1);
if(x&(1<<k))
add(x^(1<<k),k);
}
int a[2000005];
int main()
{
int n,x,ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=n;i>=1;i--)
{
int s=0,mx=0;
if(i<=n-2)
{
for(int j=20;j>=0;j--)
{
if(a[i]&(1<<j))
mx|=(1<<j);
else if(dp[s|(1<<j)][20]>1)
mx|=(1<<j),s|=(1<<j);
}
}
add(a[i],0);
ans=max(ans,mx);
}
printf("%d\n",ans);
return 0;
}