zoukankan      html  css  js  c++  java
  • 【noip2019提高组】Emiya 家今天的饭

    题面

       这里

    思路

         一道很妙的dp题,首先可以发现至多有一种主要食材被选超过t/2次,所以考虑用容斥:合法方案=总方案数-Σ每行的不合法方案数。显然:求出确定的某一列不合法的方案数比求出每一行都合法的方案数要简单得多(范围减小,限制更明确)。

           ..................这也是容斥的基本思路:正难则反....................

          根据这个可以列出朴素的dp转移方程:令f[i][j][k]表示前i行在当前第p列中选了j个,其它列中选了k个;s[i]表示第i行总共有多少道菜。

               f[i][j][k]=f[i-1][j][k]+a[i][p]*f[i-1][j-1][k]+(s[i]-a[i][k])*f[i-1][j][k-1]

           但是方程转移是n^3的,加上枚举每一列,总时间复杂度是O(m*n^3)的,很明显过不了这题。接下来考虑优化:

          再仔细读题,不合法的方案是选当前列次数超过了选其他列的个数,也就是说,我们只需要记录j和k的相对数量,于是可以将这两维压成一维。令f[i][j]表示前i列选当前行比其他行多j次的方案数。转移方程基本同上。

           剩下的问题就是如何求总方案:本人比较蒟蒻地又用了一个dp。g[i][j]表示前i行选了j次的方案数。g[i][j]=g[i-1][j]+g[i-1][j-1]*s[i];总方案即Σg[n][i]。但其实有更简单的方法:总方案=Π(s[i]+1)-1;即每行选任意一道菜,也可不选。但要减去总共选0道菜的方案。

    代码

    几个细节:

    *枚举每列时记得清空f数组

    *注意ans-f[n][i]时因为取了模可能减为负数

    *f[i][j]的j这一维可能为负数,为了好处理,将j变为j+n;转移时只需从n-i枚举到n+i,容斥时只需从n+1枚举到n+n即可

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 using namespace std;
     4 const int inf=998244353;
     5 long long f[105][305],g[105][105],s[105],ans;
     6 int a[105][2005];
     7 int main(){
     8     int i,j,n,m,k,l;
     9     scanf("%d%d",&n,&m);
    10     for(i=1;i<=n;i++){
    11         for(j=1;j<=m;j++){
    12             scanf("%d",&a[i][j]);
    13             s[i]=(s[i]+a[i][j])%inf;
    14         }
    15     }
    16     g[0][0]=1;
    17     for(i=1;i<=n;i++){
    18         g[i][0]=1;
    19         for(j=1;j<=n;j++)
    20             g[i][j]=(g[i-1][j]+g[i-1][j-1]*s[i]%inf)%inf;
    21     }
    22     for(i=1;i<=n;i++)
    23     ans=(ans+g[n][i])%inf;
    24     for(k=1;k<=m;k++){
    25         memset(f,0,sizeof(f));
    26         f[0][n]=1;
    27         for(i=1;i<=n;i++){
    28             for(j=n-i;j<=n+i;j++){
    29                 f[i][j]=(f[i][j]+f[i-1][j]+f[i-1][j-1]*a[i][k]%inf+f[i-1][j+1]*(s[i]-a[i][k])%inf)%inf;
    30             }
    31         }
    32         for(i=n+1;i<=n+n;i++)
    33         ans=(ans-f[n][i]+inf)%inf;
    34     }
    35     printf("%lld",ans);
    36     return 0;
    37 }
    View Code
  • 相关阅读:
    Oracle存储过程
    Oracle触发器
    Oracle伪列
    Oracle索引
    Oracle视图
    Oracle数据库链路
    异常处理之动手动脑
    四则运算自动出题之javaweb版
    继承与多态之动手动脑
    javaweb之添加学生信息
  • 原文地址:https://www.cnblogs.com/jstcao/p/14044121.html
Copyright © 2011-2022 走看看