zoukankan      html  css  js  c++  java
  • 【BZOJ3294】放棋子(动态规划,容斥,组合数学)

    【BZOJ3294】放棋子(动态规划,容斥,组合数学)

    题面

    BZOJ
    洛谷

    题解

    如果某一行某一列被某一种颜色给占了,那么在考虑其他行的时候可以直接把这些行和这些列给丢掉。
    那么我们就可以写出一个(dp)
    (f[i][r][c])表示考虑了前(i)种颜色,还剩下(r)(c)列没被染色。
    那么转移的时候枚举一下当前颜色染了(a)(b)列转移就好了。
    但是问题来了,怎么计算用(K)个棋子恰好覆盖(a)(b)列的方案数呢?
    恰好很不好算,那么我们换一下,至多覆盖了(a)(b)列的方案数。
    那么这个很容易算出来是(C_{ab}^{K})
    那么我们可以容斥计算恰好覆盖了(a)(b)列的方案数。
    我们在计算(a,b)的时候就已经可以算出来恰好覆盖了(l,l<a)(r,r<b)列的方案数,
    那么直接拿总数减去不合法的就好了。
    接下来就是一个很简单的(dp)了,稍微用组合数算一下即可。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define MOD 1000000009
    #define MAX 35
    inline int read()
    {
        int x=0;bool t=false;char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        if(ch=='-')t=true,ch=getchar();
        while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
        return t?-x:x;
    }
    void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
    int n,m,c,ans;
    int f[MAX][MAX][MAX],a[MAX];
    int jc[MAX*MAX],jv[MAX*MAX],inv[MAX*MAX];
    int g[MAX][MAX][MAX];
    int C(int n,int m){if(m>n)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
    int main()
    {
    	n=read();m=read();c=read();
    	for(int i=1;i<=c;++i)a[i]=read();
    	jc[0]=inv[0]=inv[1]=jv[0]=1;
    	for(int i=1;i<=n*m;++i)jc[i]=1ll*jc[i-1]*i%MOD;
    	for(int i=2;i<=n*m;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
    	for(int i=1;i<=n*m;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
    	for(int i=1;i<=c;++i)
    		for(int j=1;j<=n;++j)
    			for(int k=1;k<=m;++k)
    			{
    				if(j*k<a[i])continue;
    				g[i][j][k]=C(j*k,a[i]);
    				for(int l=1;l<=j;++l)
    					for(int r=1;r<=k;++r)
    						if(l!=j||r!=k)add(g[i][j][k],MOD-1ll*C(j,l)*C(k,r)%MOD*g[i][l][r]%MOD);
    			}
    	f[0][0][0]=1;
    	for(int i=1;i<=c;++i)
    		for(int j=1;j<=n;++j)
    			for(int k=1;k<=m;++k)
    				for(int a=1;a<=j;++a)
    					for(int b=1;b<=k;++b)
    						add(f[i][j][k],1ll*g[i][a][b]*f[i-1][j-a][k-b]%MOD*C(n-j+a,a)%MOD*(C(m-k+b,b))%MOD);
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)add(ans,f[c][i][j]);
    	printf("%d
    ",ans);
    	return 0;
    }
    						
    
    
  • 相关阅读:
    leetcode【dynamic】【0】
    VIM 常用指令
    《Java多线程编程核心技术》笔记
    log4j配置文件详解
    CSU 1803 2016(同余公式)2016年湖南省第十二届大学生计算机程序设计竞赛
    NYOJ 1233 差值(字符串排序+大数减法)
    HDU 5723 Abandoned country(最小生成树+DFS)
    POJ 1451 T9 字典树
    POJ 2965 The Pilots Brothers' refrigerator 状态压缩+广搜
    POJ 1753 Flip game状态压缩+广搜
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9416457.html
Copyright © 2011-2022 走看看