BZOJ 3294: [Cqoi2011]放棋子(計數dp)
阿新 • • 發佈:2019-02-16
\n oid mat getchar() get 思路 efi mem span 。然後把\(g\)也算到\(f\)裏去即可,時間復雜度\(O(n^2m^2c)\)
傳送門
解題思路
設\(f[i][j][k]\)表示前\(k\)個顏色的棋子占領了\(i\)行\(j\)列的方案數,那麽轉移時可以枚舉上一個顏色時占領的位置,\(f[i][j][k]=\sum\limits_{l=1}^n\sum\limits_{j=1}^mf[l][r][k-1]*C(n-l,i-l)*C(m-r,j-r)\),但發現這樣會少一個\(k\)這種顏色占領的方案數,再設\(g[i][j][k]\)表示用相同顏色的\(k\)個棋子占領\(i\)行\(j\)列的方案數,而算\(g\)時可以考慮用總的情況減去不符合的情況,即\(g[i][j][k]=C(i*j,k)-g[l][r][k]*C(i,l)*C(j,r)\)
代碼
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #define int long long using namespace std; const int N=35; const int MOD=1e9+9; typedef long long LL; inline int rd(){ int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) f=ch==‘-‘?0:1,ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-‘0‘,ch=getchar(); return f?x:-x; } int n,m,c,cnt[15],C[N*N][N*N]; int f[N][N][15],g[N][N],ans; inline void prework(){ C[0][0]=1; for(int i=1;i<=n*m;i++){ C[i][0]=1; for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD; } } signed main(){ n=rd(),m=rd(),c=rd(); prework(); for(int i=1;i<=c;i++) cnt[i]=rd(); f[0][0][0]=1; for(int o=1;o<=c;o++){ memset(g,0,sizeof(g)); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)if(i*j>=cnt[o]){ g[i][j]=C[i*j][cnt[o]]; for(int l=1;l<=i;l++) for(int r=1;r<=j;r++)if(i>l || j>r){ g[i][j]-=(LL)g[l][r]*C[i][l]%MOD*C[j][r]%MOD; g[i][j]=(g[i][j]%MOD+MOD)%MOD; } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int l=0;l<i;l++) for(int r=0;r<j;r++)if((i-l)*(j-r)>=cnt[o]) { f[i][j][o]+=(LL)f[l][r][o-1]*g[i-l][j-r]%MOD*C[n-l][i-l]%MOD*C[m-r][j-r]%MOD; f[i][j][o]%=MOD; } } // for(int i=1;i<=n;i++) // for(int j=1;j<=m;j++) // for(int o=1;o<=c;o++) // printf("f[%d][%d][%d]=%d\n",i,j,o,f[i][j][o]); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) ans=(ans+f[i][j][c])%MOD; printf("%lld\n",ans); return 0; }
BZOJ 3294: [Cqoi2011]放棋子(計數dp)