【CQOI2011】放棋子
题面
https://www.luogu.org/problem/P3158
题解
首先,对于此题,不同的行列之间应该是独立的(因为每一行每一列只能放一种颜色)
一些同色棋子对它们所在的行和列是支配的关系。
设$F[i][j][k]$为考虑了前$i$种颜色,它们支配了$i$行$j$列的方案数。(这里,一开始写的时候,我没有记录第一维,用背包的方式转移,但其实是没有“这个颜色不选”这种选择的,所以最少也要用滚动数组优化一下)
然后算$g[i][j]$,$k$个点的颜色,恰好支配了$i$行$j$列的方案数,我们先用组合数算最多,然后把没有支配到的去掉就可以了。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define mod 1000000009 #define LL long long #define ri register int using namespace std; int c[1000][1000]; int n,m,yourc,g[15][50][50],f[15][50][50]; int cnt[255]; int mul(int a,int b) { LL c=a*1LL*b; return c%mod; } int main(){ scanf("%d %d %d",&n,&m,&yourc); c[0][0]=1; for (ri i=1;i<=n*m;i++) for (ri j=0;j<=i;j++) if (j==0 || j==i) c[i][j]=1; else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; for (ri i=1;i<=yourc;i++) scanf("%d",&cnt[i]); memset(g,0,sizeof(g)); for (ri ki=1;ki<=yourc;ki++) { for (ri i=1;i<=n;i++) for (ri j=1;j<=m;j++) { if (cnt[ki]>i*j) continue; if (cnt[ki]<max(i,j)) continue; g[ki][i][j]=c[i*j][cnt[ki]]; for (ri ii=1;ii<=i;ii++) for (ri jj=1;jj<=j;jj++) if (ii!=i || jj!=j) { g[ki][i][j]-=mul(mul(c[i][ii],g[ki][ii][jj]),c[j][jj]); g[ki][i][j]+=mod; g[ki][i][j]%=mod; } } } f[0][0][0]=1; for (ri k=1;k<=yourc;k++) for (ri i=n;i>=1;i--) for (ri j=m;j>=1;j--) for (ri ii=1;ii<=i;ii++) for (ri jj=1;jj<=j;jj++) { if (g[k][ii][jj]) { f[k][i][j]+=mul(mul(mul(f[k-1][i-ii][j-jj],c[n-(i-ii)][ii]),c[m-(j-jj)][jj]),g[k][ii][jj]); f[k][i][j]%=mod; } } LL ans=0; for (ri i=1;i<=n;i++) for (ri j=1;j<=m;j++) ans+=f[yourc][i][j],ans%=mod; cout<<ans<<endl; }