Codeforces 165E Compatible Numbers

求补集。题意:给出一列长为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 }
View Code

 

posted @ 2013-11-20 21:44  浙西贫农  阅读(228)  评论(0编辑  收藏  举报