一共有 n 张卡牌,每张卡牌上有一个正整数 a[i] ,每次可以从中选出 k 张卡牌。一种选取方案的幸运值为这 k 张卡牌上数的按位异或和。
求所有选取方案的幸运值之和,对998244353取模。
1.拆位(qwq考场上因为拆位整体偏左一位挂了整整50pts)
2.对于每一个二进制位,先不考虑怎么选出来 k 个,而是考虑每个0/1对答案有多少贡献
3.对于每一位上的01串而言,先考虑所有1的贡献,因为异或操作相当于看是否有奇数个一。当我们要选出奇数个1(假设本位有a个1),组合数。剩下的 k-i 从0中选即可
TLE 90 pts
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1000010;
const int mod=998244353;
int n,m,k,a[N],num[N],c[N],cc[N],ans;
int qp(int aa,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*aa%mod;
aa=aa*aa%mod;
b>>=1;
}
return res;
}
int C(int a,int b)
{
if(a<b) return 0;
if(b==0) return 1;
if(a==b) return 1;
return (c[a]*qp(c[b],mod-2)%mod)*qp(c[a-b],mod-2)%mod;
}
signed 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",&num[i]);
// int tmp=num[i],j=0;
// while(tmp)
// {
// a[++j]+=(tmp&1);
// tmp>>=1;
// }
// m=max(m,j);
for(int j=0;j<=31;j++) if(num[i]&(1<<j)) a[j]++;
}
// printf("%lld\n",m);
// for(int i=1;i<=m;i++) printf("%lld ",a[i]);
c[0]=c[1]=1;
for(int i=2;i<=n;i++) c[i]=(c[i-1]*i)%mod;
cc[n]=qp(c[n],mod-2)%mod;
for(int f=0;f<=31;f++)//从后往前推位数 m_max=31
for(int i=1;i<=k;i+=2)
ans=(ans+(C(a[f],i)*C(n-a[f],k-i)%mod)*qp(2,f)%mod)%mod;
printf("%lld\n",ans);
return 0;
}
预处理出逆元(AC)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1000010;
const int mod=998244353;
int n,m,k,a[N],num[N],c[N],cc[N],ans;
int qp(int aa,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*aa%mod;
aa=aa*aa%mod;
b>>=1;
}
return res;
}
int C(int a,int b)
{
if(a<b) return 0;
return (c[a]*cc[b]%mod)*cc[a-b]%mod;
}
signed 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",&num[i]);
// int tmp=num[i],j=0;
// while(tmp)
// {
// a[++j]+=(tmp&1);
// tmp>>=1;
// }
// m=max(m,j);
for(int j=0;j<=31;j++) if(num[i]&(1<<j)) a[j]++;
}
// printf("%lld\n",m);
// for(int i=1;i<=m;i++) printf("%lld ",a[i]);
c[0]=c[1]=1;
for(int i=2;i<=n;i++) c[i]=(c[i-1]*i)%mod;
cc[n]=qp(c[n],mod-2)%mod;
for(int i=n;i>=1;i--) cc[i-1]=cc[i]*i%mod;
cc[1]=1;
for(int f=0;f<=31;f++)//从后往前推位数 m_max=31
for(int i=1;i<=k;i+=2)
ans=(ans+(C(a[f],i)*C(n-a[f],k-i)%mod)*qp(2,f)%mod)%mod;
printf("%lld\n",ans);
return 0;
}