幸运值

https://www.luogu.com.cn/problem/T120293

Description

一共有n张卡牌,每张卡牌上有一个数Ai,每次可以从中选出k张卡牌。一种选取方案的幸运数为这k张卡牌上数的异或和。马克想知道所有选取方案的幸运数之和除以998244353的余数。

Solution

由异或的性质,我们发现每一位的答案与选了哪些数无关,而与哪一位的0、1数量有关。

于是我们可以拆位,统计出这 N个数的每一位的0、1的个数。

  • 对于每一位,我们设有 x 个 1 ,那么则有 n−x 个 0 。

  • 要在其中选 k个数,又发现其异或和只与 1 的个数的奇偶性有关。

    这一位的$ ans=sum_{j=1,j≡1(mod 2)}{x}C_x{j}*C_{n-x}^{k-j}$

    注意特判x>y的情况

    时间复杂度为$ N log A_i$

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #define N 100010
    #define mod 998244353
    #define ll long long
    using namespace std;
    int n,k,cn[32];
    ll jc[N],ny[N];
    ll pow(ll a,ll b){
    	ll ans=1;
    	while(b){
    		if(b&1) ans=ans*a%mod;
    		a=a*a%mod;
    		b>>=1;
    	}
    	return ans;
    }
    ll c(int m,int n){
    	if(m<n) return 0;
    	return jc[m]*ny[m-n]%mod*ny[n]%mod;
    }
    int main(){
    	scanf("%d %d",&n,&k);
    	jc[0]=ny[0]=1;
    	for(int i=1;i<=n;i++){
    		int x;
    		scanf("%d",&x);
    		for(int j=0;j<31;j++)
    			if(x&(1<<j)) cn[j]++;
    		jc[i]=jc[i-1]*i%mod,ny[i]=pow(jc[i],mod-2); 
    	}
    	ll ans=0;
    	for(int j=0;j<31;j++){
    		ll tmp=0;
    		for(int i=1;i<=k;i+=2)
    			tmp=(tmp+c(cn[j],i)*c(n-cn[j],k-i)%mod)%mod;
    		ans=(ans+(1<<j)%mod*tmp%mod)%mod;
    	}
    	printf("%lld",ans);
    	return 0;
    } 
    
原文地址:https://www.cnblogs.com/ke-xin/p/13553665.html