[CQOI2011]放棋子 (DP,数论)

[CQOI2011]放棋子



$ solution: $

看到这道题我们首先就应该想到有可能是DP和数论,因为题目已经很有特性了(首先题面是放棋子)(然后这一题方案数很多要取模)(而且这一题的数据范围很小)

但真正实用的还是分析题目性质:这一道题我们仔细读题,可以发现每一种棋子的影响是相对独立的(即我们只需要知道这种颜色的棋子占了多少行列,而不需要知道它占的哪一行那一列)(这个可以画画图自证一下),而且每一种颜色占多少行和多少列也有方案数(我占两行两列,可以用两个棋子,也可以用三个或四个棋子)(而且即使如此,它还是相对独立的)

所以我们不难列出一种DP状态转移,我用 $ f[i][j][k] $ 表示前k种棋子占了i行j列的方案数

$ f[i][j][k] = sum limits_{l = 0}^{i - 1} sum limits_{r = 0}^{j - 1}f[l][r][k - 1] imes g[i-l][j-r][a[k]] $ (用 a[k] 枚棋子占 (i - l) 行 (j - r) 列的方案数)

$ imes C_{n - l}^{i - l} imes C_{m - r}^{j - r} $ (因为独立,我们可以任选 $ a[k] $ 枚棋子占用的行和列)

但是我们发现这样转移的时候,我们还需要知道(用 a[k] 枚棋子占 (i - l) 行 (j - r) 列的方案数)这个东西其实我们可以在前面就先预处理出来,状态转移十分好写!



$ code: $

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>

#define ll long long
#define db double
#define rg register int

using namespace std;

const int mod=1e9+9;

int n,m,c,ans;
int g[35][35];
int C[905][905];
int f[31][31][11];

inline int qr(){
    char ch;
    while((ch=getchar())<'0'||ch>'9');
    int res=ch^48;
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+(ch^48);
    return res;
}

int main(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    n=qr(); m=qr(); c=qr();
    for(rg i=0;i<=900;++i)C[i][0]=1;
    for(rg i=1;i<=900;++i)
        for(rg j=1;j<=i;++j)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    f[0][0][0]=1;
    for(rg k=1;k<=c;++k){
        rg x=qr();
        for(rg i=1;i<=n;++i){
            for(rg j=1;j<=m;++j){
                g[i][j]=0;
                if(i*j<x)continue;
                g[i][j]=C[i*j][x];
                for(rg l=1;l<=i;++l){
                    for(rg r=1;r<=j;++r){
                        if(l==i&&r==j)continue;
                        g[i][j]-=(ll)g[l][r]*C[i][l]%mod*C[j][r]%mod;
                        if(g[i][j]<mod)g[i][j]+=mod;
                    }
                }
            }
        }
        for(rg i=1;i<=n;++i){
            for(rg j=1;j<=m;++j){
                for(rg l=0;l<i;++l){
                    for(rg r=0;r<j;++r){
                        if((i-l)*(j-r)<x)continue;
                        f[i][j][k]+=(ll)f[l][r][k-1]*g[i-l][j-r]%mod*C[n-l][i-l]%mod*C[m-r][j-r]%mod;
                        if(f[i][j][k]>mod)f[i][j][k]-=mod;
                    }
                }
            }
        }
    }
    for(rg i=1;i<=n;++i)
        for(rg j=1;j<=m;++j)
            (ans+=f[i][j][c])%=mod;
    printf("%d
",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/812-xiao-wen/p/10685730.html