求补集。题意:给出一列长为n(n<=10^6)的数ai(0<=ai<=4*10^6)。对每个数ai,有一个询问:在这列数中是否存在一个数aj,是的ai&aj=0。
对于一个数,比如说90(1011010),对每一位取反,得到37(0100101)。显然,37&90=0,如果数列中存在37的话,那么就找到了这样的一个数aj了,当然,如果存在(0100101)的补集,这个数字也是可以找到的,比如说36(0100100),有另外的7个数(就是把1变成0就是了)也是可以的。
那么现在的问题转化为如何来求这些补集,显然,如果对于每一个集合如果直接枚举,假设这个集合里有k个1的话,则复杂度将达到o(n*2^k),这还不如直接暴力(n^2)来的快。。其实,对于一个暂时为空的集合,它完全有可能是一个非空集合的子集,从低位到高位对这个空集做与操作,这个与出来的数所对应的集合(设为A)如果非空的话,那么这个集合就是A集的子集了。4*10^6<(1<<22) 最多22位,这样做的复杂度就是o(22*10^6),可以接受。
1 #include <cstdio> 2 int a[1000010],vis[1<<22]; 3 int main(){ 4 int n,mask = (1<<22)-1; 5 scanf("%d",&n); 6 for(int i = 0;i < n;i++){ 7 scanf("%d",&a[i]); 8 vis[mask^a[i]] = a[i]; 9 } 10 for(int i = mask;i >= 0;i--){ 11 if(vis[i]) continue; 12 for(int j = 0;j < 22;j++){ 13 if(vis[i|(1<<j)]){ 14 vis[i] = vis[i|(1<<j)]; 15 break; 16 } 17 } 18 } 19 for(int i = 0;i < n;i++){ 20 if(vis[a[i]]) printf("%d ",vis[a[i]]); 21 else printf("-1 "); 22 } 23 }