【题解】与查询 [51nod1406]
【题目描述】
给出 (n) 个整数,对于 (x in [0,1000000]),分别求出在这 (n) 个整数当中同 (x) 求与之后结果为 (x) 的有多少个。
【样例】
样例输入:
3
2 3 3
样例输出:
3
2
3
2
0
0
...
...
...
0
(一共1000001个数,后面一共999997个0)
【数据范围】
(100\%) (1 leqslant N leqslant 10^6,) (1 leqslant a[i] leqslant 10^6)
【分析】
用 (dp[i]) 表示与 (i) 求与等于 (i) 的数的个数。
首先,一个数同它自己求与还是等于它自己,所以对于,(dp[i]) 的初始值就是给出的序列中 (i) 出现的次数。
对于某一个二进制数 (a),将其任意一个为 (0) 的位变为 (1) ,得到二进制数 (b)。
可知:
同 (a) 求与等于 (a) 的数同 (b) 求与不一定等于 (b),
同 (b) 求与等于 (b) 的数同 (a) 求与一定等于 (a)。
即 (b) 的方案一定可以满足 (a) 的需求。
所以 (dp) 方程为:(dp[j]+=dp[j oplus (1<<i)]),其中 (j in [0,10^6],) (j) (And (1<<i)==0) 且 (j oplus (1<<i) leqslant 10^6)
由于数据输出过多,需要自己写快输,否则会全 (TLE) 。
时间复杂度为 (O(MlogM)),其中 (M=10^6) 。
【Code】
#include<algorithm>
#include<cstdio>
#define Re register int
const int N=1e6;
int n,a,dp[N+3];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline void out(int x){
if(x>9)out(x/10);
putchar(x%10+'0');
}
int main(){
in(n);
for(Re i=1;i<=n;i++)in(a),++dp[a];
for(Re i=0;i<20;i++)
for(Re j=0;j<=N;++j)
if(!(j&(1<<i))&&(j^(1<<i))<=N)dp[j]+=dp[j^(1<<i)];
for(Re j=0;j<=N;++j)out(dp[j]),puts("");
}