BZOJ3294: [Cqoi2011]放棋子

Description

 

Input

输入第一行为两个整数nmc,即行数、列数和棋子的颜色数。第二行包含c个正整数,即每个颜色的棋子数。所有颜色的棋子总数保证不超过nm

Output

输出仅一行,即方案总数除以 1,000,000,009的余数。

Sample Input

4 2 2
3 1

Sample Output

8

HINT

N,M<=30 C<=10 总棋子数<=250

我们发现因为不能同行同列颜色不同的格子,所以我们相当于是将整张棋盘的行列分给这C种棋子。

这样我们设g[n][m][k]表示前k种棋子放完,占据了n行m列的方案数。g[n][m][k]=sum(g[n-x][n-y][k-1]*C(x,n)*C(y,n)*f[x][y][k])。

其中f[x][y][k]表示第i种棋子刚好放在了x行y列棋盘的方案数,要求每行每列都至少有一个棋子。

不难发现f数组可以用容斥原理来计算,枚举有多少行列没放棋子,转移方程也很简单。

#include<cstdio>
#include<cctype>
#include<queue>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
typedef long long ll;
const int maxn=35;
const int maxm=255;
const int mod=1000000009;
ll C[1010][1010],f[maxn][maxn][11],g[maxn][maxn][11];
int n,m,c,A[maxn];
void init() {
    C[0][0]=1;
    rep(i,1,1000) {
        C[i][0]=C[i][i]=1;
        rep(j,1,i-1) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }
}
int main() {
    n=read();m=read();c=read();
    rep(i,1,c) A[i]=read();
    init();
    rep(i,1,n) rep(j,1,m) rep(k,1,c) if(i<=A[k]&&j<=A[k]&&i*j>=A[k]){
        f[i][j][k]=C[i*j][A[k]];ll& ans=f[i][j][k];
        rep(i1,1,i) rep(j1,1,j) if(i1!=i||j1!=j) (ans-=(f[i1][j1][k]*C[i][i1]%mod)*C[j][j1])%=mod;
        ans=(ans%mod+mod)%mod;
    }
    g[0][0][0]=1;
    rep(i,1,n) rep(j,1,m) rep(k,1,c) {
        ll& ans=g[i][j][k];
        rep(i1,1,i) rep(j1,1,j) (ans+=(((f[i1][j1][k]*C[i][i1]%mod)*C[j][j1])%mod)*g[i-i1][j-j1][k-1])%=mod;
    }
    ll ans=0;
    rep(i,1,n) rep(j,1,m) (ans+=C[n][i]*C[m][j]%mod*g[i][j][c]%mod)%=mod;
    printf("%lld
",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/5303374.html