题意:
就是给你n个未知数xi,和一个数字s,然后这n个未知数的和<=s。然后再给你一个n数组,每个未知数xi>=va[i]。然后给你k次询问,每次询问把第a个数变成b后,这x个未知数有多少种不同的分配方案。
思考:
- 感觉就是以前做过的,总和分成s个未知数,不过这个题有个限制就是每个数组要>=va[i]。那么其实我可以先把每个数都变成对应的va[i],比如一共用了sum,那么还剩m-sum个,现在还有n个未知数。也就是m-sum个小球,n个盒子,然后把这些小球分配到盒子里面就可以了,那么就是经典的,球相同盒子不同的问题,方案数就是C(n+m-1,n-1)。之前整理过:牛客月赛17-计数。
- 对于剩下m-sum个是不一定要用完的,所以要求m-sum个方案数,发现每次都是从0到m-sum。所以可以先预处理一个前缀和,然后对于k查询,每次输出对应的前缀和就可以了。
- 不过这个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;
}
总结:
多多思考。