题目描述
传送门
题目大意:在一个n*m的棋盘中放入一些颜色不同的棋子,每个格子最多只能放一个棋子,不同颜色的棋子不能放在同一行或同一列,求合法的方案数。
题解
相当于每行每列只能被一种颜色占据。
那么我们可以给每个颜色分配行列数。
g[p][i][j]
表示第p中颜色占据i行j列的方案数。
如果能求出g,那么我们就可以做二维背包.
f[t][i+k][j+l]+=f[t−1][i][j]∗g[t][k][l]∗C[n−i][k]∗C[m−j][l]
如何求g,直接用组合数可能会使算出的方案存在空行或空列,所以我们需要容斥一下。
g[p][i][j]=C[i∗j][c]−C[i][x]∗C[j][y]∗g[p][x][y]
其中x,y不能同时等于i,j。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 33
#define LL long long
#define p 1000000009
using namespace std;
LL C[1003][1003],g[N][N][N],f[N][N][N];
int n,m,k;
int main()
{
freopen("a.in","r",stdin);
scanf("%d%d%d",&n,&m,&k);
for (int i=0;i<=1000;i++) C[i][0]=1;
for (int i=1;i<=1000;i++)
for (int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
for (int t=1;t<=k;t++) {
int c; scanf("%d",&c);
for (int i=0;i<=n;i++)
for (int j=0;j<=m;j++) {
g[t][i][j]=C[i*j][c];
for (int k=0;k<=i;k++)
for (int l=0;l<=j;l++)
if (k!=i||l!=j) g[t][i][j]=(g[t][i][j]-C[i][k]*C[j][l]%p*g[t][k][l]%p)%p;
}
}
f[0][0][0]=1;
for (int t=1;t<=k;t++)
for (int i=0;i<=n;i++)
for (int j=0;j<=m;j++)
for (int k=0;k<=n;k++)
for (int l=0;l<=m;l++)
if (i+k<=n&&j+l<=m) {
f[t][i+k][j+l]+=f[t-1][i][j]*g[t][k][l]%p*C[n-i][k]%p*C[m-j][l]%p;
f[t][i+k][j+l]%=p;
}
LL ans=0;
for (int i=0;i<=n;i++)
for (int j=0;j<=m;j++) ans=(ans+f[k][i][j])%p;
printf("%I64d\n",(ans%p+p)%p);
}