[CQOI2011] 放棋子 - 计数dp

在一个 \(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<

你可能感兴趣的:([CQOI2011] 放棋子 - 计数dp)