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; }