[组合计数] 分发食物(第三场模拟赛T1)

文章讨论如何通过编程解决分配n种食物给k名选手的方案数问题,涉及C(n,k)组合计算,以及优化代码以减少时间复杂度。
第一题 分发食物
提交文件 :
food.cpp
输入文件 :
food.in
输出文件 :
food.out
时间空间限制:
2 , 512 MB
在你参加本次比赛的时候,小叶老师正在为选手准备晚餐。
现在一共有 n 种食物,他计划对于第 i 种食物订购 a i 份。
比赛结束后,他将把所有的食物分给 k 位选手(不能有剩余)。每位选手可以领取不同种类的食物,但每
种食物最多只能领取一份,也可能领取不到任何食物。
现在小叶老师想知道一共有多少种分发食物的方案。
由于他不知道参赛选手的具体数量,您需要计算 k = 1 , 2 · · · m 的答案。
由于答案可能很大,你只需要输出答案对 998244353 取模的结果。
输入格式
第一行读入两个个正整数 n, m
第二行 n 个数字 a i 依次表示每种食物的数量
输出格式
输出共 m 行。
i 行一个整数表示 k = i 时的答案。
样例数据
food.in
food.out
4 6
0 1 2 3
0
0
9
96
500
1800
k = 3 时, 9 种可能的分配方案有
{1, 2, 3} {2, 3} {3} {1, 2, 3} {3} {2, 3} {1, 3} {2, 3} {2, 3}
{2, 3} {1, 2, 3} {3} {2, 3} {1, 3} {2, 3} {3} {1, 2, 3} {2, 3}
{2, 3} {2, 3} {1, 3} {2, 3} {3} {1, 2, 3} {3} {2, 3} {1, 2, 3}
数据范围
对于 30 % 的数据, 1 n, m 5 · 10 3
对于 100 % 的数据, 1 n, m 5 · 10 4 , 0 a i 10 5 , n
i =1 a i 10 5

30pt代码

单独考虑每种食物分发给k个人有多少中方案。

即C(k,ai),因为这个问题可以转化为ai个球投进k个箱子的方案。

然后根据乘法原理就可以求出了。

#include<bits/stdc++.h>
using namespace std;
int mod = 998244353;const int maxn = 1e7+10;
int fac[maxn],ifac[maxn];
int pow_mod(int a,int b){
	int ret = 1;
	for(;b;a = (long long)a*a%mod, b >>= 1){
		if((b & 1)){
			ret = (long long)ret*a%mod;
		}
	}
	return ret;
}
int C(int n,int k){
    if(k > n)return 0;
	return (long long)fac[n]*ifac[k] %mod * ifac[n-k]%mod ;	
}
void init(){
	fac[0] = 1;
	for(int i = 1;i < maxn;i++){
		fac[i] = (long long)fac[i-1]*i%mod;
	}
	ifac[maxn-1] = pow_mod(fac[maxn-1],mod-2);
	for(int i = maxn-1;i >= 1;i--) ifac[i-1] = (long long)ifac[i] *i %mod;
}
int n,m,a[50100];
vector<int> res[50010];
int main(){
	freopen("food.in","r",stdin);
	freopen("food.out","w",stdout);
    init();
    //cout<<C(3,2);
    cin>>n>>m;
    int Max = 0;
    for(int i = 1;i <= n;i++){
    	cin>>a[i];
    	Max = max(a[i],Max);
	}

	for(int i = 1;i <= m;i++){
		if(Max > i){
			cout<<0<<endl;
			continue;
		}
		long long ans = 1; 
		for(int j = 1;j <= n;j++){
			//cout<<ans<<" "<<i<<" "<<j<<" "<<C(i,a[j])<<endl;
	        ans = (ans*C(i,a[j]))%mod;
		}
		cout<<ans<<endl;
		
	}
//	for(int i = 1;i <= m;i++){
//		cout<<dp[i][n]<<endl;
//	}
	return 0;
}

100pt 思路

把a[j]存起来,用C(i,a_j)^q来加快计算(q指和a_j一样的数量出现了几次,这样a_j就和其他和a_j一样的压缩在了一起省了时间复杂度),可以发现时间复杂度变为O(n sqrt(n)).

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值