2021威海ccpc G 组合计数

题意:

n n n种食物,分给 k k k个队,每个队拿取食物时每种食物最多拿一份,可以不拿,当 k = 1 , 2.... m k=1,2....m k=1,2....m时,有多少种分配方法使得所有食物都分配给了所有队? ( ∑ i = 1 n a [ i ] ≤ 100000 ) (\sum_{i=1}^{n}a[i]\leq100000) (i=1na[i]100000)

Solution:

m a x 1 = m a x ( a [ i ] ) max1=max(a[i]) max1=max(a[i])

既然每个队最多每种拿一份,那么当 k ≥ m a x 1 k\geq max1 kmax1时才可能分配完全,此时每种食物 i i i必然分配给 k k k个队的是 a [ i ] a[i] a[i]个1和 k − a [ i ] k-a[i] ka[i]个0,这样每个队在这种食物的分配可能就有 C k a [ i ] C_{k}^{a[i]} Cka[i]种,那么总可能即 Π i = 1 n C k a [ i ] \Pi_{i=1}^{n}C_{k}^{a[i]} Πi=1nCka[i],这样的话计算一个 k k k的时间复杂度是 O ( n ) O(n) O(n),那么总复杂度是 O ( n m ) O(nm) O(nm)

正解只是加了一个小优化,因为 a [ i ] a[i] a[i]总和不超过 100000 100000 100000,这样必然会有一些 a [ i ] a[i] a[i]一样的,于是把这些 a [ i ] a[i] a[i]一样的写在一起,他们的乘积就可以用快速幂,我用了一个 m a p map map保存,每次都遍历完 m a p map map的元组,时间复杂度是 O ( 能过 ) O(能过) O(能过),用了800+ms,把 m a p map map写成数组加桶形式应该会更快

#include
using namespace std;

using ll=long long;
const int N=5e4+5;
const long long mod=998244353;

ll qpow(ll a,ll b)
{
	ll ret=1,base=a;
	while(b)
	{
		if(b&1) ret=ret*base%mod;
		base=base*base%mod;
		b>>=1;
	}
	return ret;
}

ll fac[100005],invfac[100005];
ll inv(ll k){return qpow(k,mod-2);}
ll C(int n,int m){return fac[n]*invfac[m]%mod*invfac[n-m]%mod;}

int n,m,a[N],max1;
map<int,int>map1;

int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		max1=max(max1,a[i]);
		map1[a[i]]++;
	}
	for(int i=(invfac[0]=fac[0]=1);i<=N;i++)
	{
		fac[i]=fac[i-1]*i%mod;
		invfac[i]=inv(fac[i]);
	}
	for(int i=1;i<max1;i++) printf("0\n");
	for(int i=max1;i<=m;i++)
	{
		ll ans=1;
		for(auto j:map1) ans=ans*qpow(C(i,j.first),1ll*j.second)%mod;
		printf("%lld\n",ans);
	}
	return 0;
}

你可能感兴趣的:(算法,c++)