1. 程式人生 > 其它 >「CF451E」題解

「CF451E」題解

Description

CF451E Devu and Flowers


有限制多重集組合模板題。
可轉化題目

\(n\) 種盒子,每個盒子可以放的小球數最多為 \(f_i\),盒子可為空,求總共放 \(s\) 個球的方案數,答案對 1e9+7 取模。

發現直接求方案不好做,但是總方案和不合法的方案都容易算出,考慮容斥。

總方案十分經典,可以考慮成 \(s\) 個小球,\(n-1\) 塊隔板,隔板可相鄰(某個盒子為空),問隔開的方案。
那麼因為隔板可以相鄰,直接把隔板當成小球考慮,選出 \(n-1\) 個小球的方案,即\(\binom{s+n-1}{n-1}\)

到這一步可以看出組合數的下項非常大但上項極小,使用一個常用的技巧,即把上下項約掉一部分後再分開運算。

\[\binom{s+n-1}{n-1}=\frac{(s+n-1)!}{s!(n-1)!}=\frac{(s+n-1)!}{s!}\times\frac{1}{(n-1)!} \]

前一項只用運算 \(n-1\) 次,後一項用逆元預處理可 \(\Theta(1)\) 求得,所以單次求組合數時間複雜度為 \(\Theta(n)\)
解決了組合數的問題,接下來就是很 naive 的容斥了,設 \(\operatorname{G[i]}\) 表示第 \(i\) 個盒子不合法的方案,\(S\) 表示總方案,則

\[Ans=S-\sum_{i=1}^nG[i]+\sum_{i=1}^n\sum_{j=i+1}^nG[i]G[j]-\sum_{i=1}^n\sum_{j=i+1}^n\sum_{k=j+1}^nG[i]G[j]G[k]...+(-1)^m\sum_{i=1}^n\sum_{j=i+1}^n\sum_{k=j+1}^n...\sum_{m=\alpha}^nG[i]G[j]G[k]...G[m] \]

在列舉每個盒子不合法情況時顯然不能直接球的個數,但是我們知道 \(f_i+1\)

個球的時候一定已經不合法。
那麼我們直接把這些球放進第 \(i\) 個盒子,剩下的球繼續分的都是不合法的方案。

\[G[i]=\binom{s-f_i+n-1}{n+1} \]

多個盒子時同理。
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.}\)