[CQOI2011]放棋子

题目描述

在一个m行n列的棋盘里放一些彩色的棋子,使得每个格子最多放一个棋子,且不同
颜色的棋子不能在同一行或者同一列。有多少祌方法?

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1: 复制

4 2 2
3 1

输出样例#1: 复制

8

说明

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


题解

组合计数没想出来==
但是这种题想明白了也不难啊><
可以发现同一行/列最多只能有一种颜色的棋子,
所以不需要考虑棋子具体放在哪里
所以可以设\(f[i][j][k]\)表示已经放完前\(k\)种颜色的棋子,已经占满了\(i\)\(j\)列的方案数
那么\(f[i][j][k] = \sum_{a=0}^{i}\sum_{b=0}^{j}{f[a][b][k-1]*C(n-a,i-a)*C(m-b,j-b)*(用num[k]个棋子放在这些(i-a)*(j-b)个格子且每行每列都有棋子的方案数)}\)
答案就是\(\sum_{i=1}^{n}\sum_{j=1}^{m}{f[i][j][c]}\)
我们发现用\(num[k]\)个棋子放在这些\((i-a)*(j-b)\)个格子的方案数能很快的求出
但是有一个限制是每行每列都要有棋子,所以就不能直接计算了
可以设一个\(g[i][j][k]\)表示用\(k\)个棋子填满\(i\)\(j\)列且每行每列都有棋子
那么可以简单的容斥一下,\(g[i][j][k]=C(i*j,k)-\sum_{a=1}^{i}\sum_{b=1}^{i}{g[a][b][k] * C(i,a) * C(j,b) * [a != i || b != j]}\)
这样就可以求出来了

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
# define int long long
const int M = 35 ;
const int N = 2005 ;
const int mod = 1e9 + 9 ;
using namespace std ;

int n , m , e , Ans , num[M] , f[M][M][N] , g[M][M][N] , c[N][N] ;
# undef int
int main() {
# define int long long
    scanf("%lld%lld%lld",&n,&m,&e) ;
    for(int i = 1 ; i <= e ; i ++) scanf("%lld",&num[i]) ;
    c[0][0] = 1 ;
    for(int i = 1 ; i <= 2000 ; i ++) {
        c[i][0] = 1 ;
        for(int j = 1 ; j <= i ; j ++)
            c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod ;
    }
    for(int i = 1 ; i <= n ; i ++)
        for(int j = 1 ; j <= m ; j ++)
            for(int k = max(i , j) ; k <= i * j ; k ++) {
                g[i][j][k] = c[i * j][k] ;
                for(int a = 1 ; a <= i ; a ++)
                    for(int b = 1 ; b <= j ; b ++) {
                    	if(a * b < k || (a == i && b == j)) continue ;
                    	g[i][j][k] = (g[i][j][k] - g[a][b][k] * c[i][a] % mod * c[j][b] % mod + mod) % mod ;
                    }
            }
    f[0][0][0] = 1 ;
    for(int i = 1 ; i <= n ; i ++)
        for(int j = 1 ; j <= m ; j ++) {
            for(int k = 1 ; k <= e ; k ++) {
                if(i * j < num[k]) continue ;
                for(int a = 0 ; a <= i ; a ++)
                    for(int b = 0 ; b <= j ; b ++)				
                        if((i - a) * (j - b) >= num[k])
                            f[i][j][k] = (f[i][j][k] + f[a][b][k - 1] * c[n - a][i - a] % mod * c[m - b][j - b] % mod * g[i - a][j - b][num[k]] % mod + mod) % mod ;
            }
            Ans = (Ans + f[i][j][e]) % mod ;
		}
	printf("%lld\n",(Ans % mod + mod) % mod) ;
    return 0 ;
}
posted @ 2019-01-18 11:39  beretty  阅读(212)  评论(0编辑  收藏  举报