# NOI.AC省選賽 第五場T1 子集,與&最大值
阿新 • • 發佈:2019-03-30
-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 子集,與&最大值