昨天看到一道好題,現與大家分享一下。
http://codeforces.com/contest/165/problem/E
題目大意:給一個序列a1~an(n<=1000000),問對於每一個ai來說,是否存在一個數x使得 ai&x==0且x是序列a 中的元素。ai的範圍是(1<=ai<=4000000)。
思路:對於一個數,比如說53,其二進位表示為 110101,取它的反,即為001010,我們知道,若一個數x使得x&53==0,則x的二進位一定是這樣一個形式:00?0?0(其中?表示可以為0也可以為1)。那麼我們設這樣一個數組 vis[x]={0,1},表示x是否可以由序列a中的元素,將其二進位中的若干0改為1後得到。比如說如果a中存在53(110101),那麼我們可以得到 (111101)61,(110111)55,(111111)63,則vis[53]=vis[55]=vis[61]=vis[63]=1,這個數列我們可以先預先處理一遍得到,因為ai最大為4000000,所以預先處理的複雜度為
(1<<K)*K(其中K為序列中最大的數其二進位表示的長度,最大為22)。那麼有了這個數組,我們就可以求出對於每一個數的答案了,首先,我們要求ai對應的數,我們首先取反,得到tmp,此時若vis[tmp]==0,我們可以肯定序列a中不存在與ai與操作後等於0的數,直接輸出-1,否則,我們可以知道一定存在一個數,那麼我們怎麼求這個數呢?
具體步驟如下:
我們首先設答案ans為0,我們枚舉tmp的每一位,如果遇到1,則我們把這一位設為0後,再看tmp是否存在,如果不存在,說明我們要找的那個數這一位上一定為1,則我們將ans的這一位賦值為1,然後將tmp恢複後繼續遍曆,如果存在,說明我們要找的數這一位可以為0(注意不是一定),則我們繼續遍曆即可。如果遇到0,直接繼續遍曆,因為我們要找的數這一位上一定為0。遍曆完之後,ans的值即為答案,輸出即可。這一步的時間複雜度為O(n*K),(K由上面定義),可以在規定的時間內跑完。具體代碼如下:
#include <iostream>#include <string.h>#include <stdio.h>#include <algorithm>#define ll long long#define maxn 4500000using namespace std;int getlen(int x){ int sum=0; while(x) { sum++; x/=2; } return sum;}int vis[maxn];int a[1000010];int main(){ //freopen("dd.txt","r",stdin); memset(vis,0,sizeof(vis)); int n,max=0,i,j; scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&a[i]); if(max<a[i]) max=a[i]; vis[a[i]]=1; } if(n==1) { printf("-1\n"); return 0; } int len=getlen(max); int limit=(1<<len)-1; for(i=0;i<=limit;i++) { if(!vis[i]) continue; for(j=0;j<len;j++) { if(((1<<j)&i)==0) { vis[i^(1<<j)]=1; } } } for(i=1;i<=n;i++) { int tmp=limit-a[i]; //printf("%d\n",tmp); if(!vis[tmp]) printf("-1 "); else { int ans=0; for(j=0;j<len;j++) { if(tmp&(1<<j)) { if(vis[tmp^(1<<j)]) tmp^=(1<<j); else { ans+=1<<j; } } } printf("%d ",ans); } } printf("\n"); return 0;}