「CF451E Devu and Flowers」题解
Description
有限制多重集组合模板题。
可转化题目
有 \(n\) 种盒子,每个盒子可以放的小球数最多为 \(f_i\),盒子可为空,求总共放 \(s\) 个球的方案数,答案对 1e9+7 取模。
发现直接求方案不好做,但是总方案和不合法的方案都容易算出,考虑容斥。
总方案十分经典,可以考虑成 \(s\) 个小球,\(n-1\) 块隔板,隔板可相邻(某个盒子为空),问隔开的方案。
那么因为隔板可以相邻,直接把小球和隔板当作空考虑,选出 \(n-1\) 个空的方案,即 \(\binom{s+n-1}{n-1}\)。
到这一步可以看出组合数的下项非常大但上项极小,使用一个常用的技巧,即把上下项约掉一部分后再分开运算。
即
前一项只用运算 \(n-1\) 次,后一项用逆元预处理可 \(\Theta(1)\) 求得,所以单次求组合数时间复杂度为 \(\Theta(n)\)。
解决了组合数的问题,接下来就是很 naive 的容斥了,设 \(\operatorname{G[i]}\) 表示第 \(i\) 个盒子不合法的方案,\(S\) 表示总方案,则
在枚举每个盒子不合法情况时显然不能直接球的个数,但是我们知道 \(f_i+1\) 个球的时候一定已经不合法。
那么我们直接把这些球放进第 \(i\) 个盒子,剩下的球继续分的都是不合法的方案。
即
多个盒子时同理。
Code:
#include<cstdio>//直接转化为球相同+隔板,且隔板的数量极少
#include<iostream>//枚举不合法时先控制成至少,因为剩下的隔板可以继续分配,然后相当于球减少了(f_i+1)个继续用求全集公式
using namespace std;//然后就变成了一个小技巧,当下项极大时拆分一下即可
const int Mod=1e9+7;
const int MAXN=25;
const int M=20;
#define ll long long
int n,o[2];
ll f[MAXN],s,fra[MAXN],ofra[MAXN],Ans;
ll ksm(ll a,int b)
{
ll ans=1;
while(b){
if(b&1) ans=ans*a%Mod;
a=a*a%Mod;b>>=1;
}
return ans;
}
ll C(ll n,int k){
if(n<k) return 0;
ll ans=1;
for(ll i=n;i>n-k;i--){
ans=ans*(i%Mod)%Mod;
}
return ans*ofra[k]%Mod;
}
int main(){
o[0]=1,o[1]=-1;
fra[0]=1;
for(int i=1;i<=M;i++){
fra[i]=fra[i-1]*i%Mod;
}
ofra[M]=ksm(fra[M],Mod-2);
for(int i=M;i;i--){
ofra[i-1]=ofra[i]*i%Mod;
}
scanf("%d%lld",&n,&s);
for(int i=1;i<=n;i++){
scanf("%lld",&f[i]);
}
Ans=C(s+n-1,n-1);
int k=1<<n;
for(int i=1;i<k;i++){
int ty=0;ll tot=0;
for(int j=0;j<n;j++){
ty+=(i>>j)&1;
if((i>>j)&1) tot+=f[j+1]+1;
}
Ans=(Ans+o[ty%2]*C(s+n-1-tot,n-1)%Mod)%Mod;
Ans=(Ans+Mod)%Mod;
}
printf("%lld",Ans);
return 0;
}
综合来看这就是用了一个小技巧和有个小细节的容斥裸题 qwq。
\(\Bbb{End.}\)
\(\Bbb{Thanks} \space \Bbb{For} \space \Bbb{Reading.}\)
本文来自博客园,作者:{StranGePants},转载请注明原文链接:https://www.cnblogs.com/StranGePants/p/15574838.html