2021威海ccpc G 组合计数

组合数学在食物分配问题的应用
本文探讨了一种特定的食物分配问题,通过组合数学的方法计算不同队伍间多种食物的分配方案数量。考虑到每种食物每队最多只能拿取一份的情况,文章提出了一种基于快速幂优化的高效算法。
题意:

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

Solution:

max1=max(a[i])max1=max(a[i])max1=max(a[i])

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

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

#include<bits/stdc++.h>
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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值