【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;
}

 

posted @ 2019-09-06 22:40  HellPix  阅读(184)  评论(0编辑  收藏  举报