zoukankan      html  css  js  c++  java
  • P3158 [CQOI2011]放棋子 计数DP+组合数学

    题意:

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

    范围&性质 (1le n,mle 30,1le kle 10)

    分析:

    • 暴力

    枚举每行每列放什么,复杂度 (O(k^{n+m}))

    • 正解

    按照颜色来考虑贡献,将每一种颜色带来的贡献单独考虑,同时我们发现方案数与行列的顺序无关,只是组合数的影响,所以我们在设 DP 状态时只需要考虑行列的个数

    (f[i][j][k]) 表示前 (k) 种颜色覆盖了 (i)(j) 列的方案,转移时需要枚举第 (k+1) 种颜色会覆盖新的行和列的个数,但是我们发现第 (k+1) 种颜色的贡献似乎不好直接表示出来

    那么我们专门来计算每个颜色的方案数,记 (g[i][j][k]) 表示用 (k) 个同样颜色的棋子,覆盖 (i)(j) 列的方案,可以用容斥的方法来计算,即总方案减去不合法方案数

    [g[i][j][k]=C_{i imes j}^k-sum_{l=1}^isum_{r=1}^jg[l][r][k] imes C_i^l imes C_j^r ]

    总方案数等于能放的 (i imes j) 里面选 (k) 个,不合法的方案等价于从当前 (i) 行里面枚举重复了 (l) 行 ,当前 (j) 列里面枚举重复了 (r)

    总的转移式就是 (f[i][j][k]=f[l][r][k-1]+g[i-l][j-r][a[k]])

    答案就是 (sum f[i][j][k]) ,因为存在空出几行没有被覆盖的情况,所以需要枚举覆盖的行和列,复杂度(O(n^2m^2k))

    代码:

    #include<bits/stdc++.h>
    
    using namespace std;
    
    namespace zzc
    {
    	inline int read()
    	{
    		int x=0,f=1;char ch=getchar();
    		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    		return x*f;
    	}
    	
    	const int maxn = 35;
    	const int maxm = 905;
    	const int mod = 1e9+9;
    	int c[maxm][maxm];
    	int f[maxn][maxn][maxm],g[maxn][maxn];
    	int n,m,k;
    	long long ans=0;
    	
    	void init()
    	{
    		c[0][0]=1;
    		c[1][0]=c[1][1]=1;
    		for(int i=2;i<=900;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;
    			}
    		}
    	}
    	
    	void work()
    	{
    		int a;
    		init();
    		n=read();m=read();k=read();
    		f[0][0][0]=1; 
    		for(int i=1;i<=k;i++)
    		{
    			a=read();
    			memset(g,0,sizeof(g));
    			for(int x=1;x<=n;x++)
    			{
    				for(int y=1;y<=m;y++)
    				{
    					if(x*y>=a)
    					{
    						g[x][y]=c[x*y][a];
    						for(int l=1;l<=x;l++)
    						{
    							for(int r=1;r<=y;r++)
    							{
    								if(l<x||r<y) g[x][y]=(g[x][y]-(long long)g[l][r]*c[x][l]%mod*c[y][r]%mod+mod)%mod;
    							}
    						}
    					}
    				}
    			}
    			for(int x=1;x<=n;x++)
    			{
    				for(int y=1;y<=m;y++)
    				{
    					for(int l=0;l<=x;l++)
    					{
    						for(int r=0;r<=y;r++)
    						{
    							int tmpx=x-l;
    							int tmpy=y-r;
    							if(tmpx*tmpy>=a)
    							{
    								f[x][y][i]=(f[x][y][i]+(long long)f[l][r][i-1]*g[tmpx][tmpy]%mod*c[n-l][tmpx]%mod*c[m-r][tmpy]%mod)%mod;
    							}
    						}
    					}
    				}
    			}
    		}
    		for(int i=1;i<=n;i++)
    		{
    			for(int j=1;j<=m;j++)
    			{
    				ans=(ans+f[i][j][k])%mod;
    			}
    		}
    		printf("%lld
    ",ans);
    	}
    
    }
    
    int main()
    {
    	zzc::work();
    	return 0;
    }
    
    
  • 相关阅读:
    你不得不知道的Visual Studio 2012(2) 全新调试功能
    我看TechEd 2012之App时代降临
    Asp.Net MVC4入门指南(1): 入门介绍
    前端代码标准最佳实践:CSS篇
    Asp.Net MVC4入门指南(2):添加一个控制器
    你不得不知道的Visual Studio 2012(1) 每日必用功能
    Asp.Net MVC4入门指南(4):添加一个模型
    光棍节程序员闯关秀过关全攻略(附带小工具)
    C#性能优化实践
    Asp.Net MVC4入门指南(3):添加一个视图
  • 原文地址:https://www.cnblogs.com/youth518/p/13984082.html
Copyright © 2011-2022 走看看