在一个 \(m\) 行 \(n\) 列的棋盘里放一些彩色的棋子,使得每个格子最多放一个棋子,且不同颜色的棋子不能在同一行或者同一列,有多少种方法?
Solution
设 \(f[i][j][k]\) 表示用前 \(k\) 种颜色的棋子,占领了 \(i\) 行 \(j\) 列的方案数
设 \(g[i][j][k]\) 表示用任意 \(k\) 个同色棋子占领 \(i\) 行 \(j\) 列的方案数,则考虑总方案数 - 实际上有没有被占领的行或列的方案数,则
\[g[i][j][k]=C_{ij}^k-\sum_{l=1}^i\sum_{r=1}^j g[l][r][k]\cdot C_i^l C_j^r \]
于是对 \(f[i][j][k]\),转移方程为
\[f[i][j][k]=\sum_{l=0}^{i-1} \sum_{r=0}^{j-1} f[l][r][k-1]\cdot g[i-l][j-r][a[k]]\cdot C_{n-l}^{i-l} C_{m-r}^{j-r} \]
注意这个转移当 \((i-l)(j-r) \ge a[k]\) 时才成立,于是答案为
\[\sum_{i=1}^n \sum_{j=1}^m f[i][j][c] \]
#include
using namespace std;
#define int long long
const int N = 35;
const int mod = 1e+9+9;
int f[N][N][N],g[N][N][N*N],n,m,c,a[N],C[N*N][N*N];
signed main() {
ios::sync_with_stdio(false);
cin>>n>>m>>c;
C[0][0]=1;
for(int i=1;i<=1000;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;
}
int mx=0;
for(int i=1;i<=c;i++) cin>>a[i], mx=max(mx,a[i]);
f[0][0][0]=1;
for(signed i=1;i<=n;i++) {
for(signed j=1;j<=m;j++) {
for(signed k=1;k<=mx;k++) {
int fg=0;
for(signed t=1;t<=c;t++) if(a[t]==k) fg=1;
if(!fg) continue;
g[i][j][k]=C[i*j][k];
for(signed l=1;l<=i;l++) {
for(signed r=1;r<=j;r++) {
if(i==l && j==r) continue;
g[i][j][k]=(g[i][j][k]-g[l][r][k]*C[i][l]%mod*C[j][r]%mod+mod)%mod;
}
}
}
}
}
for(signed i=1;i<=n;i++) {
for(signed j=1;j<=m;j++) {
for(signed k=1;k<=c;k++) {
for(signed l=0;l=a[k])
(f[i][j][k]+=f[l][r][k-1]*g[i-l][j-r][a[k]]%mod*C[n-l][i-l]%mod*C[m-r][j-r]%mod)%=mod;
}
}
}
}
}
int ans=0;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) (ans+=f[i][j][c])%=mod;
cout<