Jzoj5414 幸运值

校庆志愿者小Z在休息时间和同学们玩卡牌游戏。一共有n张卡牌,每张卡牌上有一个数Ai,每次可以从中选出k张卡牌。一种选取方案的幸运值为这k张卡牌上数的异或和。小Z想知道所有选取方案的幸运值之和除以998244353的余数。

为什么中间跳过了几道题?因为现在来不及改了先把过了的搬上来吧

套路:按位计算贡献

我们考虑,对于32个位置,选择k个异或为1的选法有多少

我们记cnt[i]表示第i位为1的数有多少个

那么显然,每一位对于答案的为2^i*方案数使得这一位为1

若要这一位为1,显然就需要选出奇数个1,那么我们假设选出了j个,那么0就要选k-j个

所以这一位答案就是2^i*(ΣC(j,cnt[i])*C(k-j,n-cnt[i]){j&1=1}

最后累加就好

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define LL __int128
#define M 998244353 
using namespace std;
int cnt[32],n,s[100010],k;
LL js[100010],inv[100010],S=0,s1,s2;
LL pow(LL x,int k){
	LL s=1;
	for(;k;x=x*x%M,k>>=1) if(k&1) s=s*x%M;
	return s;
}
LL C(int m,int n){
	if(n<0 || m>n || m<0) return 0;
	return js[n]*inv[m]%M*inv[n-m]%M;
}
int main(){
	freopen("card.in","r",stdin);
	freopen("card.out","w",stdout);
	scanf("%d%d",&n,&k); --k;
	for(int i=*js=*inv=1;i<=n;++i){
		scanf("%d",s+i);
		js[i]=js[i-1]*i%M;
		for(int j=0;s[i];++j,s[i]>>=1) cnt[j]+=(s[i]&1);
	}
	inv[n]=pow(js[n],M-2); 
	for(int i=n;i;--i) inv[i-1]=inv[i]*i%M;
	for(int i=0;i<32;++i){
		s1=s2=0;
		for(int j=0;j<=k;j+=2) s1=(s1+C(j,cnt[i]-1)*C(k-j,n-cnt[i]));
		for(int j=1;j<=k;j+=2) s2=(s2+C(j,cnt[i])*C(k-j,n-cnt[i]-1));
		S=(S+(1ll<<i)*(s1*cnt[i]%M+s2*(n-cnt[i])%M)%M)%M;
	}
	printf("%lld\n",(int)(S*pow(k+1,M-2)%M));
}

posted @ 2017-11-07 07:23  扩展的灰(Extended_Ash)  阅读(224)  评论(0编辑  收藏  举报