Description
校庆志愿者小Z在休息时间和同学们玩卡牌游戏。一共有n张卡牌,每张卡牌上有一个数Ai,每次可以从中选出k张卡牌。一种选取方案的幸运值为这k张卡牌上数的异或和。小Z想知道所有选取方案的幸运值之和除以998244353的余数。
Input
输入的第一行有两个整数n和k。
第二行有n个整数,表示序列A。
Output
一个整数表示答案。
Sample Input
输入1:
3 2
1 2 3
输入2:
10 5
123 456 789 987 654 321 101 202 303 404
Sample Output
输出1:
6
输出2:
130776
Data Constraint
对于30%的数据满足,1<=n<=20
对于另30%的数据满足,1<=n<=100,0
题解
这题其实我想出来了,然后就写挂了。
首先,每一位互不影响,然后拆位讨论。
每一位我们可以数出来0和1的个数
然后从n个01中选k个数的异或和
其实因为a^1^1=a,所以只有选奇数个1时才会有贡献(0无论如何都没有贡献)
所以直接暴力组合数算就好了。
代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#define ll long long
using namespace std;
inline void read(int &x){
x=0;int f=1;char ch=' ';
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
x*=f;
}
const int p=998244353;
inline void exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x=1;
y=0;
return;
}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
inline ll Inv(ll a,ll p){
ll x,y;
exgcd(a,p,x,y);
if(x<0)x+=p;
return x;
}
ll jie[100001];
ll inv[100001];
inline ll C(int n,int m){
if(m>n)return 0;
return (jie[n]*inv[m])%p*inv[n-m]%p;
}
int n,k;
int a[100001];
int sum[31];
ll ans;
int main(){
freopen("card.in","r",stdin);
freopen("card.out","w",stdout);
read(n);read(k);
jie[0]=inv[0]=1;
for(int i=1;i<=n;++i){
read(a[i]);
jie[i]=(jie[i-1]*i)%p;
inv[i]=Inv(jie[i],p);
for(int j=0;j<=30;++j){
if((a[i]>>j)&1)++sum[j];
}
}
for(register int j=0;j<=30;++j){
int cnt1=sum[j];
int cnt0=n-cnt1;
int lim=min(cnt1,k);
for(register int i=1;i<=lim;i+=2){
ans+=(((ll)(1<<j)*C(cnt1,i))%p)*C(cnt0,k-i)%p;
if(ans>=p)ans%=p;
}
}
printf("%lld",ans);
return 0;
}