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;
}