【BZOJ 3294】 3294: [Cqoi2011]放棋子 (DP+组合数学+容斥原理)

3294: [Cqoi2011]放棋子

Description

Input

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

Output

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

Sample Input

4 2 2
3 1

Sample Output

8

HINT

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。。

 

 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 }
View Code

 

屏蔽掉的是一开始两种错误方法。。

 

2017-03-21 08:28:26

posted @ 2017-03-21 08:28  konjak魔芋  阅读(195)  评论(0编辑  收藏  举报