2022杭州-IHI‘s Homework-(组合数+球盒问题)

I

题意:
就是给你n个未知数xi,和一个数字s,然后这n个未知数的和<=s。然后再给你一个n数组,每个未知数xi>=va[i]。然后给你k次询问,每次询问把第a个数变成b后,这x个未知数有多少种不同的分配方案。

思考:

  1. 感觉就是以前做过的,总和分成s个未知数,不过这个题有个限制就是每个数组要>=va[i]。那么其实我可以先把每个数都变成对应的va[i],比如一共用了sum,那么还剩m-sum个,现在还有n个未知数。也就是m-sum个小球,n个盒子,然后把这些小球分配到盒子里面就可以了,那么就是经典的,球相同盒子不同的问题,方案数就是C(n+m-1,n-1)。之前整理过:牛客月赛17-计数
  2. 对于剩下m-sum个是不一定要用完的,所以要求m-sum个方案数,发现每次都是从0到m-sum。所以可以先预处理一个前缀和,然后对于k查询,每次输出对应的前缀和就可以了。
  3. 不过这个C(n+i-1,n-1)的和(i从0到m),其实可以优化成一个公式:C(n+m,n)。

代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);

using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 1e6+10,M = 2010;

int T,n,m,k;
int va[N];
int pre[N];
int fact[N],infact[N];

int ksm(int a,int b)
{
	int sum = 1;
	while(b)
	{
		if(b&1) sum = sum*a%mod;
		a = a*a%mod;
		b >>= 1;
	}
	return sum;
}

void init(int x)
{
	fact[0] = infact[0] = 1;
	for(int i=1;i<=x;i++) fact[i] = fact[i-1]*i%mod;
	infact[x] = ksm(fact[x],mod-2)%mod;
	for(int i=x-1;i>=1;i--) infact[i] = infact[i+1]*(i+1)%mod;
}

int C(int a,int b)
{
	if(a<b) return 0;
	return fact[a]*infact[b]%mod*infact[a-b]%mod;
}

signed main()
{
	IOS;
	cin>>n>>m>>k;
	init(5e5);
	pre[0] = 1;
	for(int i=1;i<=m;i++) pre[i] = (pre[i-1]+C(n-1+i,n-1))%mod;
	int sum = 0;
	for(int i=1;i<=n;i++) cin>>va[i],sum += va[i];
	while(k--)
	{
		int a,b;
		cin>>a>>b;
		sum -= va[a];
		va[a] = b;
		sum += va[a];
		if(m-sum<0) cout<<0<<"\n";
		else cout<<pre[m-sum]<<"\n"; //cout<<C(n+m-sum,n)<<"\n";
	}
	return 0;
}

总结:
多多思考。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值