zoukankan      html  css  js  c++  java
  • [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
    ",(Ans % mod + mod) % mod) ;
        return 0 ;
    }
    
  • 相关阅读:
    做足以让自己骄傲的活
    Count(*) 与 count(field) 一样吗?
    Explain Plan试分析
    Oracle SQL Developer中查看解释计划Explain Plan的两种方法
    整理+学习《骆昊-Java面试题全集(上)》
    【转】Java就业指导
    如何清晰的、高质量的给面试官介绍自己的电商项目【借鉴】
    留存的图片
    Linux学习_006_JavaEE程序员常用linux命令整理
    给Linux初学者的七个建议,值得一读
  • 原文地址:https://www.cnblogs.com/beretty/p/10286872.html
Copyright © 2011-2022 走看看