传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3294
这道题的dp方程容易想到:令f[i][j][k]表示前i种颜色占了j行,k列的方案数,g[i][j][k] 表示用第i种颜色占了j行,k列的方案总数,则f[i][j][k] = sigma(f[i - 1][x][y] * g[i][j - x][k - y]);
关键是g[i][j][k]怎么求,这就是经典的容斥原理了。
先减去少一行或一列的,再加上少两行或少两列或少一行一列的......
#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long LL; const LL mod = 1000000009LL; const int maxn = 35, maxk = 15; LL f[maxk][maxn][maxn]; LL g[maxk][maxn][maxn]; LL c[maxn * maxn][maxn * maxn]; int d[maxk]; int n, m, k; LL ans; void pre() { c[0][0] = 1; for(int i = 1; i < maxn * maxn; i ++) { c[i][0] = c[i][i] = 1; } for(int i = 2; i < maxn * maxn; i ++) { for(int j = 1; j < i; j ++) { c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod; } } return; } void cal() { for(int i = 1; i <= k; i ++) { for(int a = 1; a <= n; a ++) { for(int b = 1; b <= m; b ++) { for(int x = 1; x <= a; x ++) { for(int y = 1; y <= b; y ++) { if(((a + b) & 1) == ((x + y) & 1)) { g[i][a][b] += c[x * y][d[i]] * c[a][x] % mod * c[b][y] % mod; } else { g[i][a][b] -= c[x * y][d[i]] * c[a][x] % mod * c[b][y] % mod; } if(g[i][a][b] < 0) g[i][a][b] += mod; g[i][a][b] %= mod; } } } } } return; } int main() { pre(); scanf("%d%d%d", &n, &m, &k); for(int i = 1; i <= k; i ++) { scanf("%d", &d[i]); } cal(); f[0][0][0] = 1; for(int i = 1; i <= k; i ++) { for(int a = i; a <= n; a ++) { for(int b = i; b <= m; b ++) { for(int x = max(a - d[i], 0); x < a; x ++) { for(int y = max(b - d[i], 0); y < b; y ++) { f[i][a][b] = (f[i][a][b] + f[i - 1][x][y] * g[i][a - x][b - y] % mod * c[n - x][a - x] % mod * c[m - y][b - y] % mod) % mod; } } } } } for(int i = 0; i <= n; i ++) { for(int j = 0; j <= m; j ++) { ans = (ans + f[k][i][j]) % mod; } } cout << ans << endl; return 0; }