1. 程式人生 > ># NOI.AC省選賽 第五場T1 子集,與&最大值

# NOI.AC省選賽 第五場T1 子集,與&最大值

-i 選擇判斷 所有 trie getchar() const cpp etc inline

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

# NOI.AC省選賽 第五場T1 子集,與&最大值