【YbtOJ#10052】卡牌选取

题目

题目链接:http://noip.ybtoj.com.cn/problems

思路

考虑按位计算贡献。假设第 \(i\) 位有 \(cnt0/1\)\(0/1\),那么枚举选取 \(1\) 的个数,组合数计算即可。
时间复杂度 \(O(n\log A\log \operatorname{MOD})\),其中 \(A=\max(a_i)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=100010,MOD=998244353,LG=30;
int n,m,cnt1,cnt0,a[N];
ll ans,fac[N];

ll fpow(ll x,int k)
{
	ll ans=1;
	for (;k;k>>=1,x=x*x%MOD)
		if (k&1) ans=ans*x%MOD;
	return ans;
}

ll C(ll n,ll m)
{
	ll inv=fpow(fac[m]*fac[n-m]%MOD,MOD-2);
	return fac[n]*inv%MOD;
}

int main()
{
	freopen("card.in","r",stdin);
	freopen("card.out","w",stdout);
	scanf("%d%d",&n,&m);
	fac[0]=1;
	for (int i=1;i<=n;i++)
		fac[i]=fac[i-1]*i%MOD;
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for (int i=0;i<=LG;i++)
	{
		cnt0=cnt1=0;
		for (int j=1;j<=n;j++)
			if (a[j]&(1<<i)) cnt1++;
				else cnt0++;
		for (int j=1;j<=cnt1;j+=2)
		{
			int k=m-j;
			if (cnt0<k || k<0) continue;
			ans=(ans+(1LL<<i)%MOD*C(cnt1,j)%MOD*C(cnt0,k))%MOD;
		}
	}
	printf("%lld",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/13679063.html