# NOI.AC省选赛 第五场T1 子集,与&最大值

NOI.AC省选赛 第五场T1

A. Mas的童年

题目链接

http://noi.ac/problem/309

思路

0x00

\(n^2\)的暴力挺简单的。

ans=max(ans,xor[j-1]+xor[j-1]^xor[i]);

01trie树求最大异或和相信大家都会。不会看这里.

这与我们今天这个题目有关吗?

毫无关系。

xor[i]的某一位为1,xor[j]的那一位不管是啥,贡献都是为1。

而xor[i]的某一位为0,xor[j]的贡献是2或0。(xor[j]位上为1贡献为2)

(这里的1,2都是2^(k)的1倍,两倍)

所以我们只要考虑xor[i]的位上为0的贡献。

将xor[i]取反,就是求与(&)最大值。

最后整理一下就可以计算出答案了。

0x01

如何求与(&)最大值

把每个插入的值的所有子集装进一个桶里。

然后从大到小选择判断是否在桶里面出现过,因为从大到小枚举,所以一定不会冲突掉,就好了,这看代码比较清晰。

相似的,我们也可以求或(|)的最大值

0x02

复杂度?

x如果枚举过了就不用再枚举

那复杂度就是每个数枚举下二进制

\((nlogn)\)

当然如果插入数的上限太大了话应该就做不了。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=2e6+7;
int read() {
int x=0,f=1;char s=getchar();
for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
return x*f;
}
int n,a[N],xo[N],tong[N];
void mark(int x) {//枚举子集
tong[x]=1;
for(int i=0;i<20;i++)
if((x>>i&1)&&(!tong[x^(1<<i)]))
mark(x^(1<<i));
}
int askand(int x) {//RT
int ans=0;
for(int i=19;i>=0;--i)
if(!((1<<i)&x) && tong[ans|(1<<i)]) ans|=(1<<i);
return ans;
}
int main() {
n=read();
for(int i=1;i<=n;++i) a[i]=read(),xo[i]=xo[i-1]^a[i];
for(int i=1;i<=n;++i) {
cout<<askand(xo[i])*2+xo[i]<<" ";
mark(xo[i]);
}
return 0;
}
上一篇:python(2)-字符串(2)


下一篇:《你不知道的javascript》一、函数作用域和块作用域