• BZOJ3294: [Cqoi2011]放棋子(计数Dp,组合数学)


    题目链接

    解题思路:

    发现一个性质,如果考虑一个合法的方案可以将行和列都压到一起,也就是说,在占用行数和列数一定的情况下,行列互换是不会影响答案的,那么考虑使用如下方程:

    $f[i][j][k]$为占领了i行j列使用了前k种颜色,由于要求全部用完,不需要枚举放入多少,考虑一个一个来添加颜色。考虑添加第k种颜色:

    因为第k种颜色一定是占据了新的一行一列,所以加入第k种颜色后的行数=加入之前的行数+第k种颜色占据的行数,列数同理。

    设第k种颜色的棋子有a个,那么我们只需要知道用A种颜色占据i行j列的方案数,设为$g[i][j][A]$这个可以使用容斥

    转移即为$g[i][j][A]=C_{i*j}^A-limitssum_{a=1}^{i}limitssum_{b=1}^{j}g[a][b][A]*C_i^a*C_j^b$发现和A毛关系没有就舍去了这一维。

    所以最后的转移就是:

    $f[i][j][k]=limitssum_{a=1}^{i}limitssum_{b=1}^{j}f[i-a][i-b][k-1]*g[a][b]*C_{n-i+a}^{a}*C_{m-j+b}^{b}$

    答案就是i,j的累和。

    代码:

     

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 typedef long long lnt;
     5 const lnt mod=(lnt)(1e9+9);
     6 lnt f[500][500][11];
     7 lnt g[500][500];
     8 int num[11];
     9 lnt C[1000][1000];
    10 int n,m,c;
    11 void get_g(int k)
    12 {
    13     memset(g,0,sizeof(g));
    14     int A=num[k];
    15     for(int i=1;i<=n;i++)
    16     {
    17         for(int j=1;j<=m;j++)
    18         {
    19             if(i*j>=A)
    20             {
    21                 g[i][j]=C[i*j][A];
    22                 for(int a=1;a<=i;a++)
    23                 {
    24                     for(int b=1;b<=j;b++)
    25                     {
    26                         if(a==i&&b==j)continue;
    27                         (g[i][j]-=g[a][b]*C[i][a]%mod*C[j][b]%mod)%=mod;
    28                     }
    29                 }
    30             }
    31         }
    32     }
    33     return ;
    34 }
    35 void init(void)
    36 {
    37     C[0][0]=1;
    38     for(int i=1;i<=900;i++)
    39     {
    40         C[i][0]=1;
    41         for(int j=1;j<=i;j++)
    42         {
    43             C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    44         }
    45     }
    46     return ;
    47 }
    48 int main()
    49 {
    50     scanf("%d%d%d",&n,&m,&c);
    51     for(int i=1;i<=c;i++)scanf("%d",&num[i]);
    52     init();
    53     f[0][0][0]=1;
    54     for(int k=1;k<=c;k++)
    55     {
    56         get_g(k);
    57         for(int i=1;i<=n;i++)
    58         {
    59             for(int j=1;j<=m;j++)
    60             {
    61                 if(i*j<num[k])continue;
    62                 for(int a=i;a<=n;a++)
    63                 {
    64                     for(int b=j;b<=m;b++)
    65                     {
    66                         (f[a][b][k]+=f[a-i][b-j][k-1]*g[i][j]%mod*C[n-a+i][i]%mod*C[m-b+j][j]%mod)%=mod;
    67                     }
    68                 }
    69             }
    70         }
    71     }
    72     lnt ans(0);
    73     for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)(ans+=f[i][j][c])%=mod;
    74     printf("%lld
    ",(ans%mod+mod)%mod);
    75     return 0;
    76 }
  • 相关阅读:
    Cocoa中对日期和时间的处理 NSCalendar (一)
    iOS优秀博客收录
    八大排序(内容是转载链接)
    IOS8定位无效问题
    ios 里如何判断当前应用的定位服务是否可用
    MKMapView和MKMapViewDelegate
    根据经纬度翻译成详细位置的各种方法
    iOS 根据经纬度反查 地名
    MapKit学习笔记
    解决Collection <__NSArrayM: 0xb550c30> was mutated while being enumerated
  • 原文地址:https://www.cnblogs.com/blog-Dr-J/p/10540108.html
Copyright © 2020-2023  润新知