F - Bits And Pieces
Something about SOS dp
题意:给定序列a,求
m
a
x
(
a
i
∣
(
a
j
&
a
k
)
)
1
≤
i
<
j
<
k
≤
n
max(a_i |(a_j\&a_k))1\leq i < j <k \leq n
max(ai∣(aj&ak))1≤i<j<k≤n
分析: 对于位运算求最大值的题目,一般采用从最高位向最低位依次填充贪心的做法。
分析
∣
|
∣和
&
\&
&的性质,可以知道, 可以枚举
a
i
a_i
ai,然后从最高位贪心,判断有一个bit位
a
j
,
a
k
a_j,a_k
aj,ak存在两个数位1就可以继续放,问题在于我们怎么保证第一次取一个bit位的有一些集合
s
s
s满足条件,下一次取从
s
s
s这个集合中取,而不是其它不符合条件中取。这种就是经典的SOSdp,SOSdp求的是
∑
m
a
s
k
&
i
=
i
B
[
i
]
\sum_{mask\&i = i}B[i]
mask&i=i∑B[i]B[i]是指值为i的
a
a
a的个数
本题求得是
∑
m
a
s
k
&
i
=
m
a
s
k
B
[
i
]
\sum_{mask\&i=mask}B[i]
mask&i=mask∑B[i]
只需要将sosdp的状态转移反过来就OK了
const int maxn = 1e6+10;
int a[maxn];
int dp[1<<21][21];
void update(int num,int k){
if(k > 20) return ;
if(dp[num][k] > 1) return ;
++dp[num][k];
update(num,k+1);
if(num>>k&1)
update(num^(1<<k),k);
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;cin>>n;
for(int i = 1;i <= n; ++i)
cin>>a[i];
int ans = 0;
for(int i = n;i >= 1; --i){
int res = 0;
int t = 0;
for(int j = 20;j >= 0; --j){
if(a[i]>>j&1)
res |= 1<<j;
else if(dp[t|(1<<j)][20] > 1)
res |= 1<<j,t |= 1<<j;
}
update(a[i],0);
if(i <= n-2)
ans = max(ans,res);
}
cout<<ans<<endl;
return 0;
}