Codeforces 848D - Shake It!(DP)
hot tea 一道。
首先我們考慮這個奇奇怪怪的最小割有什麼等價的表達。不難發現,如果我們選擇了 \(S\to T\) 這條邊並加入了一個新的節點 \(u\),那麼就會出現兩條邊 \(S\to u,u\to T\)。我們考慮把 \(S\) 和 \(u\) 分別當作新的源點和匯點重複上面的過程,假設 \(S\to u\) 產生的流量為 \(f_1\),我們再把 \(u,T\) 也分別當作新的源匯併產生 \(f_2\) 的流量,那麼新產生的這個節點 \(u\) 對原圖的最小割,即最大流產生了 \(\min(f_1,f_2)\) 的貢獻。也就是說,當我們選擇 \(S\to T\)
考慮 \(f_{n,m}\) 表示對於一張初始只有 \(S,T\) 兩個點和一條邊 \(S\to T\) 的圖進行 \(n\) 次操作後能夠得到多少張最小割為 \(m\) 的圖,再設 \(g_{n,m}\) 表示對於一張初始有三個點 \(S,T,u\) 和兩條邊 \(S\to u\) 和 \(u\to T\) 的圖在進行 \(n-1\) 次操作後可以得到多少個最小割為 \(m\) 的圖,轉移就考慮對 \(S\to u\)
接下來考慮怎樣 \(g\to f\),方便起見,我們將所有 \(S\to u,u\to T\) 進行 \(n-1\) 次操作得到的最小割為 \(m\) 的圖稱作一個“\((n,m)\) 結構”,將所有 \((n,m)\) 結構的總體稱作“\((n,m)\) 類”,那麼我們考慮一個揹包的思想,考慮所有 \((i,j)\) 類對 \(f_{n,m}\) 的貢獻,那麼我們列舉用了多少個 \((i,j)\) 類中的結構,設為 \(k\),那麼有轉移 \(f_{n,m}\leftarrow f_{n-ki,m-kj}·\dbinom{g_{i,j}+k-1}{k}\),其中後面那個組合數可以用隔板法來解釋,具體來說就是設 \((i,j)\) 類第 \(t\) 個結構出現了 \(x_t\) 次,那麼由於“經過置換得到的圖視為相同”這一條件的存在,一組 \((x_1,x_2,\cdots,x_{g_{i,j}})\) 就能唯一確定一張圖,方案數就是 \(x_1+x_2+\cdots+x_{g_{i,j}}=k\) 的解的個數,根據隔板法可知該值等於 \(\dbinom{g_{i,j}+k-1}{k}\)。
還有一個小問題就是 DP 轉移的順序,如果我們不欽定 DP 轉移的順序就會算重。因此我們考慮從小到大列舉 \(i\) 再從小到大列舉 \(j\),算出 \(g_{i,j}\) 之後再用多重揹包的方式鬆弛所有 \(f_{n,m}\),不難發現這樣我們肯定會按照 \((i,j)\) 這樣的二元組的字典序順序進行多重揹包,也就不會擔心算重的問題了。這就有點類似於子集卷積那種“半線上”的感覺,學過子集卷積/半線上卷積的應該會比較好理解。
時間複雜度上界大概是 \(n^4\ln n\),因為後面列舉 \(k\) 那一維複雜度大概是調和級數的。
const int MAXN=50;
const int MOD=1e9+7;
int n,m,f[MAXN+5][MAXN+5],sf[MAXN+5][MAXN+5],g[MAXN+5][MAXN+5],sg[MAXN+5][MAXN+5];
int inv[MAXN+5];
int main(){
scanf("%d%d",&n,&m);f[0][1]=sf[0][1]=1;
for(int i=(inv[0]=inv[1]=1)+1;i<=MAXN;i++) inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++){
for(int j=1;j<=n+1;j++) for(int k=0;k<=i-1;k++)
sg[i][j]=(sg[i][j]+1ll*sf[k][j]*sf[i-1-k][j])%MOD;
for(int j=1;j<=n+1;j++) g[i][j]=(sg[i][j]-sg[i][j+1]+MOD)%MOD;
// for(int j=1;j<=n+1;j++) printf("g %d %d %d\n",i,j,g[i][j]);
for(int j=1;j<=n+1;j++){
for(int k=n+1;k;k--) for(int l=n+1;l;l--){
int mul=1;
for(int t=1;t*i<=k&&t*j<=l;t++){
mul=1ll*mul*(g[i][j]+t-1)%MOD*inv[t]%MOD;
f[k][l]=(f[k][l]+1ll*f[k-t*i][l-t*j]*mul)%MOD;
}
}
}
for(int j=n+1;j;j--) sf[i][j]=(sf[i][j+1]+f[i][j])%MOD;
} printf("%d\n",f[n][m]);
return 0;
}