hdu 4810 Wall Painting(组合数学)

首先把所有数都转换成二进制,然后可以对二进制的每一位分别来计算异或和,假设第i位N个数中有a个是1,b个是0,那么只有选奇数个1时异或和是1,选偶数个时异或和就是零。

所以就是在a个数中挑1,3,5...K个数,b个数中挑K-1,K-3......个0的方案数,这个数可以用组合数算出来。再用这个数乘上这一位2进制的权值(1<<i)就是这一位产生的方案的和,注意会爆int。


还有一个坑点是在预处理组合数时,要用C[N][K]=C[N-1][K]+C[N-1][K-1]这个公式算,否则用乘法公式算的时候mod之后再除可能会取整了。这里wa了N次。


代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define LL long long
#define mod (1000003)

int a[100];
int N;
LL c[1005][1005];

void get(int n){
	int pos=1;
	while(n){
		if(n&1) a[pos]++;
		n>>=1;
		pos++;
	}
}


int main(){
	memset(c,0,sizeof(c));
	c[0][0]=1;
	c[1][0]=1;
	c[1][1]=1;
	for(int i=2;i<=1000;i++){
		c[i][0]=1;
		for(int j=1;j<=i;j++){
			c[i][j]=c[i-1][j]+c[i-1][j-1];
			c[i][j]%=mod;

		}
	}


	while(~scanf("%d",&N)){
		memset(a,0,sizeof(a));
	    for(int i=1;i<=N;i++){
			LL tmp;
			scanf("%I64d",&tmp);
			get(tmp);
		}

		LL res=0;
		for(int i=1;i<=N;i++){
			res=0;
		    for(int j=1;j<=80;j++){
				if(!a[j]) continue;
				for(int k=1;k<=a[j];k+=2){
					if(k>i) break;
					LL tmp=c[a[j]][k]*c[N-a[j]][i-k];
					tmp%=mod;
					LL tmp2=(1<<(j-1));
					tmp2%=mod;
					tmp*=tmp2;
					tmp%=mod;
					res+=tmp;
					res%=mod;
				}

		    }
			printf("%I64d",res);
			if(i!=N) printf(" ");
			else printf("\n");
		}
	}
	return 0;
}


你可能感兴趣的:(组合数)