题目
题目描述
校庆志愿者小Z在休息时间和同学们玩卡牌游戏。一共有n张卡牌,每张卡牌上有一个数Ai,每次可以从中选出k张卡牌。一种选取方案的幸运值为这k张卡牌上数的异或和。小Z想知道所有选取方案的幸运值之和除以998244353的余数。
数据范围
对于
30
%
30\%
30%的数据满足,
1
≤
n
≤
20
1\le n\le20
1≤n≤20
对于另
30
%
30\%
30%的数据满足,
1
≤
n
≤
100
1\le n\le 100
1≤n≤100,
0
<
A
i
≤
1024
0<A_i\le1024
0<Ai≤1024
对于
80
%
80\%
80%的数据满足,
1
≤
n
≤
2000
1\le n\le2000
1≤n≤2000
对于
100
%
100\%
100%的数据满足,
1
≤
n
≤
100000
,
0
<
A
i
<
2
31
,
1
≤
k
≤
n
1\le n\le100000,0<A_i<2^{31},1\le k\le n
1≤n≤100000,0<Ai<231,1≤k≤n
题解
题目大意
给出 n n n个数,在其中选 k k k个数求异或和,问所有情况异或和的总和模998244353的结果
题目分析
异或的套路一般都是按位来处理,这题也是。求出每个位置上为1有多少个数,然后根据组合数得出公式(设当前位置 j j j上1的个数是 x x x)
a n s = ∑ i = 1 m i n ( k , x ) ( x i ) ( n − x k − i ) 2 j ans=\sum_{i=1}^{min(k,x)}\begin{pmatrix}x\\i\end{pmatrix}\begin{pmatrix}n-x\\k-i\end{pmatrix}2^j ans=∑i=1min(k,x)(xi)(n−xk−i)2j
解释:
首先一定要保证 i i i是奇数,因为只有是奇数当前位置才能有值。那么可以在当前位置是1的 x x x的位置中选择 i i i个,方案 C x i C_x^i Cxi
然后在剩下的 n − x n-x n−x个当前位置为0的数中选取 k − i k-i k−i个,方案为 C n − x k − i C_{n-x}^{k-i} Cn−xk−i
最后乘上当前位置的值 2 j 2^j 2j即可
Code
#include<cstdio>
#define ll long long
#define p 998244353
using namespace std;
ll ans,n,k,x,a[150000],jc[150000];
ll ksm(ll x,ll y)
{
int res=1;
while (y)
{
if (y&1) res=res*x%p;
x=x*x%p;
y>>=1;
}
return res;
}
ll C(ll x,ll y) {return (jc[x]*ksm(jc[y],p-2)%p)*ksm(jc[x-y],p-2)%p;}
int main()
{
freopen("card.in","r",stdin);
freopen("card.out","w",stdout);
scanf("%lld%lld",&n,&k);
for (int i=1;i<=n;++i)
scanf("%lld",&a[i]);
jc[0]=1;
for (ll i=1;i<=n;++i)
jc[i]=jc[i-1]*i%p;
for (int j=1;j<=31;++j)
{
x=0;
for (ll i=1;i<=n;++i)
if (a[i]&(1<<(j-1))) ++x;
for (ll i=1;i<=k&&i<=x;i+=2)
ans=(ans+(C(x,i)*C(n-x,k-i)%p)*(1<<(j-1))%p)%p;
}
printf("%lld\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}