【BZOJ 3294】 3294: [Cqoi2011]放棋子 (DP+组合数学+容斥原理)
3294: [Cqoi2011]放棋子
Description
Input
输入第一行为两个整数n, m, c,即行数、列数和棋子的颜色数。第二行包含c个正整数,即每个颜色的棋子数。所有颜色的棋子总数保证不超过nm。
Output
输出仅一行,即方案总数除以 1,000,000,009的余数。
Sample Input
4 2 2
3 1Sample Output
8HINT
N,M<=30 C<=10 总棋子数<=250
Source
【分析】
表示一开始看错题ORZ。。以为相同颜色的不能放一起【这样怎么做??
然后就是其实题目不是这样的、、、
DP[i][j][k]表示决策到第k种颜色,前k种颜色一共占了i行j列的方案数。
枚举第k行占的行数和列数,ii,jj,那么dp[i][j][k]=f[i-ii][j-jj][k-1]*B[ii][jj][k]*C[n-(i-ii)][ii]*C[m-(j-jj)][jj]
其中C是组合数,B[ii][jj][k]表示用ii行jj列填k个东西的方案(注意B数组要满足每一行每一列都有东西,不然好像很容易算重复)
对于B数组,我一开始用了两种方法求,都不对(超容易算重复smg,然后很内伤)
最后感觉只有枚举这一种方法是可以求出来的,
就是递推 B[x][y][z]=C[x*y][k]-sigma(B[i][j][k]*C[x][i]*C[y][j]) (1<=i<=x&&1<=j<=y&&(i!=x||j!=y))
【这里是容斥吧?
组合数学没学好所以我这题又做了很久ORZ。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 #define Mod 1000000009 8 #define Maxn 910 9 #define LL long long 10 11 LL sm[15],f[40][40][15]; 12 int n,m,c; 13 14 LL C[Maxn][Maxn]; 15 16 void get_c() 17 { 18 memset(C,0,sizeof(C)); 19 for(int i=0;i<=n*m;i++) C[i][0]=1; 20 for(int i=1;i<=n*m;i++) 21 for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%Mod; 22 } 23 24 LL B[40][40][15]; 25 LL get_B(int x,int y,int z) 26 { 27 if(B[x][y][z]!=-1) return B[x][y][z]; 28 if(x==0&&sm[z]==0) {B[x][y][z]=1;return 1;} 29 if(sm[z]<x||sm[z]<y||x*y<sm[z]) {B[x][y][z]=0;return 0;} 30 LL ans=0; 31 ans=C[x*y][sm[z]]; 32 for(int i=1;i<=x;i++) 33 for(int j=1;j<=y;j++) 34 { 35 if(i==x&&j==y) continue; 36 // ans++; 37 LL X=get_B(i,j,z)%Mod, 38 Y=(C[x][i]*C[y][j])%Mod; 39 ans=ans-X*Y;ans%=Mod; 40 ans=(ans+Mod)%Mod; 41 } 42 // printf("B[%d][%d][%d]=%d\n",x,y,z,ans); 43 /*for(int i=1;i<=z;i++) 44 ans=(ans+get_B(x-1,y,z-i)*C[y][i])%Mod; 45 printf("B[%d][%d][%d]=%d\n",x,y,z,ans);*/ 46 47 /*ans=C[n*m][x];ans%=Mod; 48 ans-=C[(n-1)*m][x]*n;ans%=Mod; 49 ans-=C[n*(m-1)][x]*m;ans%=Mod; 50 ans+=C[(n-1)*(m-1)][x]*n*m;ans%=Mod; 51 ans=(ans+Mod)%Mod;*/ 52 B[x][y][z]=ans; 53 return ans; 54 } 55 56 int main() 57 { 58 scanf("%d%d%d",&n,&m,&c); 59 for(int i=1;i<=c;i++) scanf("%d",&sm[i]); 60 get_c(); 61 memset(f,0,sizeof(f)); 62 memset(B,-1,sizeof(B)); 63 f[0][0][0]=1; 64 LL ans=0; 65 for(int i=1;i<=n;i++) 66 for(int j=1;j<=m;j++) 67 for(int k=1;k<=c;k++) get_B(i,j,k); 68 for(int k=1;k<=c;k++) 69 { 70 for(int i=1;i<=n;i++) 71 for(int j=1;j<=m;j++) 72 { 73 for(int ii=1;ii<=i;ii++) 74 for(int jj=1;jj<=j;jj++) 75 { 76 LL X=(C[n-(i-ii)][ii]*C[m-(j-jj)][jj])%Mod, 77 Y=(f[i-ii][j-jj][k-1]*B[ii][jj][k])%Mod; 78 f[i][j][k]=(f[i][j][k]+X*Y)%Mod; 79 } 80 81 // f[i][j][k]=(f[i][j][k]+(C[n-(i-ii)][ii]*C[m-(j-jj)][jj])%Mod*(f[i-ii][j-jj][k-1]*C[ii*jj][sm[k]])%Mod)%Mod; 82 if(k==c) ans=(ans+f[i][j][k])%Mod; 83 // printf("f[%d][%d][%d]=%lld\n",i,j,k,f[i][j][k]); 84 } 85 } 86 printf("%lld\n",ans); 87 return 0; 88 }
屏蔽掉的是一开始两种错误方法。。
2017-03-21 08:28:26