[ACM]【容斥原理/揹包DP】牛客練習賽64 寶石裝箱

寶石裝箱

傳送門
題意:n個物品裝入n個盒子,每個盒子都要裝一個物品。第i個物品不能裝進第aia_i個盒子。求合法的裝法數。
圖

思路:

第一眼看到這題,心想這不就是傳說中的錯排題目嗎?
回憶一下錯排題目的解法:

DnD_nnn個物品錯排的方案數目。有D1=0D_1=0D2=1D_2=1
n3n\geq 3時,設nn排在第kk位,其中knk\not =n,也就是1kn11\leq k\leq n-1。那現在考慮kk的情況:
(1)當kk放在第nn位時,錯排數=Dn2=D_{n-2}
(2)當kk,沒排在第nn位時,此時等價於Dn1D_{n-1}
kk有從11n1n-1n1n-1種取法。
所以答案爲Dn=(n1)(Dn1+Dn2)D_n=(n-1)(D_{n-1}+D_{n-2})

可惜的是,這道題沒這麼簡單(。
注意哦,對於每一個盒子,可能有多個物品不能放進這裏。而對於每個物品,他可以放進除了該盒子以外的所有盒子。

這時候dalao們就要想到容斥原理的作法了。
容斥精講博文
如何容斥呢?
先認識一下容斥原理的公式吧:

設 U 種元素有 n 種不同的屬性,而第 i 種屬性稱爲PiP_i,擁有屬性PiP_i的元素構成集合SiS_i,那麼
i=1nSi=iSii<jSiSj+i<j<kSiSjSk+(1)m1ai<ai+1i=1mSai++(1)n1S1Sn|\bigcup_{i=1}^nS_i|=\sum_i|S_i|-\sum_{i<j}|S_i\cap S_j|+\sum_{i<j<k}|S_i\cap S_j\cap S_k|-\cdots +(-1)^{m-1}\sum_{a_i<a_{i+1}}|\bigcap_{i=1}^mS_{a_i}|+\cdots+(-1)^{n-1}|S_1\cap\cdots\cap S_n|

i=1nSi=m=1n(1)m1ai<ai+1i=1mSai|\bigcup_{i=1}^n S_i|=\sum_{m=1}^n(-1)^{m-1}\sum_{a_i<a_{i+1}}|\bigcap_{i=1}^mS_{a_i}|

我們的目的是求得所有盒子都合法的方案個數
Si\overline{S_i}表示第ii個盒子合法的方案集合。答案用集合表示就是i=1nSi|\bigcap_{i=1}^n\overline {S_i}|。(”|“表示元素個數還記得嗎)
由補集,我們知道
i=1nSi=Ui=1nSi|\bigcap_{i=1}^n\overline {S_i}|=|U|-|\bigcup_{i=1}^n{S_i}|
易知U=n!|U|=n!,因此,我們可以通過計算i=1nSi|\bigcup_{i=1}^n{S_i}|得到答案。
i=1nSi|\bigcup_{i=1}^n{S_i}|可以由容斥原理轉化爲
m=1n(1)m1ai<ai+1i=1mSai\sum_{m=1}^n(-1)^{m-1}\sum_{a_i<a_{i+1}}|\bigcap_{i=1}^mS_{a_i}|

因此,只需要求出來ai<ai+1i=1mSai\sum_{a_i<a_{i+1}}|\bigcap_{i=1}^mS_{a_i}|即可。
觀察一遍容斥原理的定義,把條式子轉化爲文字描述:
在n個盒子中,每“m個盒子不合法的並集”的總和。

仔細理解這句描述,因爲在這句描述中,只考慮了m個盒子的狀態,此時其他(n-m)個盒子的狀態如何,是無所謂的。他們可以是合法的,亦可以是非法的。因爲容斥過了,m=1n(1)m1ai<ai+1i=1mSai\sum_{m=1}^n(-1)^{m-1}\sum_{a_i<a_{i+1}}|\bigcap_{i=1}^mS_{a_i}|這整個式子會把重複的方案減掉。因此,這句描述可以理解爲,每“至少m個盒子不合法的並集”的總和。

我們可以通過求每“m個盒子不合法的並集”的總和(不考慮其他盒子的是非)來求至少:在求到不考慮其他盒子的值之後,只需要乘上(nm)!(n-m)!即可。

那如何求剛好呢?
通過動態規劃。設DP(i,j)DP(i,j)爲考慮到第ii個盒子的時候,已經發現jj個盒子非法的方案數目,DP(n,0)=1DP(n,0)=1DP(0,0)=1DP(0,0)=1(因爲我們不考慮其他的盒子的是非)。我們得到狀態轉移:
DP(i,j)=DP(i1,j)+DP(i1,j1)b[i]DP(i,j)=DP(i-1,j)+DP(i-1,j-1)*b[i]
此處的b[i]b[i]爲當前盒子要非法時,可以選擇的物品數目。注意,因爲我們的DPDP考慮的是已經發現非法的方案數目,因此這個b[i]b[i]的選取並無後效性。(對於每個物品,他只有一個盒子不能放,這b[i]b[i]個物品不可能出現在狀態DP(i1,j1)DP(i-1,j-1)j1j-1個盒子之中)

答案爲Um=1n(1)m1ai<ai+1i=1mSai|U|-\sum_{m=1}^n(-1)^{m-1}\sum_{a_i<a_{i+1}}|\bigcap_{i=1}^mS_{a_i}|
n!+m=1n(1)mDP(n,m)×(nm)!n!+\sum_{m=1}^n(-1)^mDP(n,m)\times (n-m)!
m=0n(1)mDP(n,m)×(nm)!\sum_{m=0}^n(-1)^mDP(n,m)\times (n-m)!

在實現的過程中,用了空間優化。

代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=8004;
const int mod=998244353;
ll dp[maxn],b[maxn],fac[maxn];
int main(){
	int n;
	fac[0]=1;
	for(int i=1;i<=maxn;i++) fac[i]=fac[i-1]*i%mod;
	scanf("%d",&n);
	int tmp;
	for(int i=1;i<=n;i++){
		scanf("%d",&tmp);
		b[tmp]++;
	}
	dp[0]=1;
	for(int i=1;i<=n;i++){
		for(int j=i;j>=1;j--){
			dp[j]=(dp[j]+dp[j-1]*b[i])%mod;
		}
	}
	ll ans=0;
	for(int i=0;i<=n;i++){
		if(i&1) ans-=dp[i]*fac[n-i];
		else ans+=dp[i]*fac[n-i];
		ans=(ans+mod)%mod;
	}
	printf("%lld\n",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章