BZOJ 3294: [Cqoi2011]放棋子
3294: [Cqoi2011]放棋子
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 628 Solved: 238
[Submit][Status][Discuss]
Description
Input
输入第一行为两个整数n, m, c,即行数、列数和棋子的颜色数。第二行包含c个正整数,即每个颜色的棋子数。所有颜色的棋子总数保证不超过nm。
Output
输出仅一行,即方案总数除以 1,000,000,009的余数。
Sample Input
4 2 2
3 1
3 1
Sample Output
8
HINT
N,M<=30 C<=10 总棋子数<=250
Source
分析:
上课不好好听课的我TAT...
此题最重要的思想感觉是补集转化思想...
f[i][j][k]代表前k种颜色占据了i行j列的方案数,那么怎么转移...
f[i][j][k]=Σf[i-x][j-y][k-1]*g[x][y][k]*c[i][x]*c[j][y]
g[x][y][k]代表什么?第k种颜色刚好占据了x行y列...感觉这个转移还是很好想的...
但是问题来了...g[x][y][k]怎么求...
我们可以转化为总方案数减去不合法的方案数,也就是g[i][j][k]=c[i*j][num[k]]-Σg[x][y][k]*c[i][x]*c[j][y]...
注意边界...WA了好几次...QAQ...
代码:
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 //by NeighThorn 6 #define int long long 7 using namespace std; 8 9 const int maxn=30+5,MOD=1e9+9; 10 11 int n,m,co,ans,num[maxn],c[maxn*maxn][maxn*maxn],f[maxn][maxn][maxn],g[maxn][maxn][maxn]; 12 13 signed main(void){ 14 memset(f,0,sizeof(f)); 15 memset(g,0,sizeof(g)); 16 scanf("%lld%lld%lld",&n,&m,&co); 17 for(int i=1;i<=co;i++) 18 scanf("%lld",&num[i]); 19 for(int i=0;i<=900;i++) 20 c[i][0]=c[i][i]=1; 21 for(int i=2;i<=900;i++) 22 for(int j=1;j<i;j++) 23 c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD; 24 for(int k=1;k<=co;k++) 25 for(int i=1;i<=n;i++) 26 for(int j=1;j<=m;j++) 27 if(i*j>=num[k]&&max(i,j)<=num[k]){ 28 g[i][j][k]=c[i*j][num[k]]; 29 for(int x=1;x<=i;x++) 30 for(int y=1;y<=j;y++) 31 if((i-x)||(j-y)) 32 g[i][j][k]=(g[i][j][k]-g[x][y][k]*c[i][x]%MOD*c[j][y]%MOD+MOD)%MOD; 33 } 34 f[0][0][0]=1; 35 for(int k=1;k<=co;k++) 36 for(int i=1;i<=n;i++) 37 for(int j=1;j<=m;j++) 38 if(i*j>=num[k]){ 39 for(int x=1;x<=i;x++) 40 for(int y=1;y<=j;y++) 41 (f[i][j][k]+=f[i-x][j-y][k-1]*g[x][y][k]%MOD*c[i][x]%MOD*c[j][y]%MOD)%=MOD; 42 } 43 for(int i=1;i<=n;i++) 44 for(int j=1;j<=m;j++) 45 (ans+=f[i][j][co]*c[n][i]%MOD*c[m][j]%MOD)%=MOD; 46 printf("%lld\n",ans); 47 return 0; 48 }
by NeighThorn