「luogu - P3158」「cqoi 2011」放棋子
阿新 • • 發佈:2021-12-15
解讀一下,大概就是一種顏色放進去就會佔據一行一列,dp 狀態就好想了:\(f_{i,j,k}\) 表示恰好用完前 \(k\) 種顏色的所有棋子,佔據了 \(i\) 行 \(j\) 列的方案數。你把已經被佔據的行列挪到㮟㮟角角,這就匯出了一個子問題,在一個 \((n-i)\times(m-j)\) 的矩形種,使用恰好 \(u\) 個棋子(注意不是種類,這也是子問題和原問題的區別)佔據其中一些行列的方案數。
容易發現,我們比較關心的是佔據的行列數,而其與整個 \((n-i)\times(m-j)\) 矩形的關聯並不強(因為我們都可以把選出來的挪到㮟㮟角角),設佔據了 \(x\) 行 \(y\)
看回到 \(f\) 的轉移,考慮新棋子所佔據的行列數 \(h,l\),則有 \(f_{i-h,j-l,k}=\sum\sum\binom{i}{h}\binom{j}{l}f_{i,j,k-1}f'_{h,l,a_k}\)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MOD=1e9+9; int n,m,c,a[20],mx,coef[40][40]; ll fac[110],ifac[110],dp[11][50][50],dpsub[50][50][1000],ans; inline ll C(const int i,const int j) { assert(i>=j); return coef[i][j]; } char buf[1<<21],*p1=buf,*p2=buf; #define Gc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) int Gi() { int x=0; char c=Gc(); bool f=0; while(c<'0' || c>'9') f|=(c=='-'),c=Gc(); while(c>='0' && c<='9') x=x*10+(c&15),c=Gc(); return f?-x:x; } signed main() { for(int i=0; i<=35; ++i) { coef[i][0]=1; for(int j=1; j<=i; ++j) coef[i][j]=(coef[i-1][j-1]+coef[i-1][j])%MOD; } n=Gi(); m=Gi(); c=Gi(); for(int i=1; i<=c; ++i) mx=max(mx,a[i]=Gi()); dpsub[0][0][0]=1; for(int i=1; i<=n; ++i) { for(int j=0; j<=m; ++j) { for(int k=0; k<=mx; ++k) { for(int h=1; h<=m; ++h) { for(int l=0; l<=min(h,j); ++l) { if(j+h-l<=m) (dpsub[i][j+h-l][k+h]+=C(j+h-l,j)*C(j,l)%MOD*dpsub[i-1][j][k]%MOD)%=MOD; } } } } } dp[0][n][m]=1; for(int k=1; k<=c; ++k) { for(int i=1; i<=n; ++i) { for(int j=1; j<=m; ++j) { for(int h=1; h<=i; ++h) { for(int l=1; l<=j; ++l) (dp[k][i-h][j-l]+=C(i,h)*C(j,l)%MOD*dp[k-1][i][j]%MOD*dpsub[h][l][a[k]]%MOD)%=MOD; } } } } for(int i=0; i<=n; ++i) { for(int j=0; j<=m; ++j) ans+=dp[c][i][j],ans%=MOD; } printf("%lld\n",ans); return 0; }