洛谷題目鏈接:[CQOI2011]放棋子
題目描述
在一個m行n列的棋盤里放一些彩色的棋子,使得每個格子最多放一個棋子,且不同
顏色的棋子不能在同一行或者同一列。有多少祌方法?例如,n=m=3,有兩個白棋子和一
個灰棋子,下面左邊兩祌方法都是合法的,但右邊兩祌都是非法的。
輸入輸出格式
輸入格式:
輸入第一行為兩個整數n, m, c,即行數、列數和棋子的顏色數。第二行包含c個正整數,即每個顏色的棋子數。所有顏色的棋子總數保證不超過nm。
輸出格式:
輸出僅一行,即方案總數除以 1,000,000,009的余數。
輸入輸出樣例
輸入樣例#1:
4 2 2
3 1
輸出樣例#1:
8
說明
N,M<=30 C<=10 總棋子數<=250
題解: 一道\(DP\)的好題.
定義狀態\(f[i][j][k]\)表示用前\(k\)種顏色占領了任意\(i\)行\(j\)列.
設\(a[k]\)表示第\(k\)種顏色的棋子的個數.
轉移很顯然是$$f[i][j][k]=\sum_{l=0}^{i-1}\sum_{r=0}^{j-1}f[l][r][k-1]C_{n-l}^{i-l}C_{m-r}^{j-r}*用a[k]個棋子占領任意i-l行j-r列的方案數$$
那么我們再定義狀態\(g[i][j][k]\)表示用\(k\)個相同顏色的棋子占領任意\(i\)行\(j\)列的方案數,直接算不太好算,我們可以考慮容斥:$$g[i][j][k]=C_{ij}^k-\sum_{l=0}^{i}\sum_{r=0}^{j}g[l][r][k]C_{i}^{l}*C_{j}^{r},(l \not= i \ ||\ r \not= j)$$
預處理了\(g\)數組,就可以對\(f\)數組轉移了:$$f[i][j][k]=\sum_{l=0}^{i-1}\sum_{r=0}^{j-1}f[l][r][k-1]C_{n-l}^{i-l}C_{m-r}^{j-r}*g[i-l][j-r][a[k]]$$
因為不一定要放滿整個棋盤,所以$$ans=\sum_{l=1}^n\sum_{r=1}^mf[i][j][c]$$
其實還可以用滾動數組滾掉\(g\)數組的最后一維.
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int N = 30+5;
const int COL = 10+5;
const int K = 250+5;
const int mod = 1e9+9;
int n, m, C, a[N], c[1000][1000], ans = 0;
int f[N][N][COL], g[N][N][1000];
// f : k types of col occupied any i lines, j rows
// g : same type k chess piece occupied any i lines, j rows
int main(){
cin >> n >> m >> C;
for(int i = 1; i <= n; i++) cin >> a[i];
f[0][0][0] = c[0][0] = 1;
for(int i = 1; i <= 900; i++){
c[i][0] = 1;
for(int j = 1; j <= i; j++) c[i][j] = (c[i-1][j]+c[i-1][j-1])%mod;
}
for(int k = 1; k <= C; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++){
if(a[k] > i*j) continue; int res = 0;
g[i][j][a[k]] = c[i*j][a[k]];
for(int l = 1; l <= i; l++)
for(int r = 1; r <= j; r++)
if(l < i || r < j) (res += 1ll*c[i][l]*c[j][r]%mod*g[l][r][a[k]]%mod) %= mod;
g[i][j][a[k]] = (g[i][j][a[k]]-res+mod)%mod;
}
for(int k = 1; k <= C; k++)
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++)
(f[i][j][k] += 1ll*c[n-l][i-l]*c[m-r][j-r]%mod*g[i-l][j-r][a[k]]%mod*f[l][r][k-1]%mod) %= mod;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) (ans += f[i][j][C]) %= mod;
cout << ans << endl;
return 0;
}