[CQOI2011] 放棋子 - 计数dp

Posted mollnn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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}^isum_{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 <bits/stdc++.h>
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<i;l++) {
                    for(signed r=0;r<j;r++) {
                        if((i-l)*(j-r)>=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<<ans;
}

以上是关于[CQOI2011] 放棋子 - 计数dp的主要内容,如果未能解决你的问题,请参考以下文章

luogu 3158 [CQOI2011]放棋子

题解 P3158 [CQOI2011]放棋子

[CQOI2011]放棋子

[题解] [CQOI2011] 放棋子

[CQOI2011]放棋子

BZOJ 3294: [Cqoi2011]放棋子