【CSP2019】Emiya 家今天的饭

【CSP2019】Emiya 家今天的饭

题意

链接

题解

显然根据性质最多的菜品不超过总数量的一半,可以考虑补集转化

即计算有一个菜品超过数量一半的方案数,再用总方案数减

总方案数显然可以一个背包搞定(然而我去年就是因为总方案数没求对挂了)

然后考虑求超过一半的方案数

枚举当前哪个菜品是超过一半的

有一个显然的DP:

$ f_{i,j,k} $ 表示当前到第i行最多的菜品选了j个,其余的选了K个的方案数

但是这样会T

考虑能否优化

注意到我们需要的状态并不需要知道菜品的具体数量,只需要要求菜品的相对数量关系

于是可以枚举相对数量关系来压缩一维

#include<bits/stdc++.h>

using namespace std;

#define LL long long 

inline LL read()
{
    LL x= 0,f = 1;
    char ch;
    do
    {
        ch = getchar();
        if(ch == '-') f = -1;
    }while(ch <'0'||ch >'9');
    do
    {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar();
    }while(ch >= '0' && ch <= '9');
    return f*x;
}

const int MOD = 998244353;
const int MAXN = 100 + 10;
const int MAXM = 2000 + 10;

int n,m;
LL a[MAXN][MAXM];
LL tot_meal;
LL f[MAXN],s[MAXN];
LL dp[MAXN][MAXN<<1];

int main()
{
//    freopen("meal.in","r",stdin);
//    freopen("meal.out","w",stdout);
    n = read(),m = read();
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            a[i][j] = read();
            s[i] += a[i][j];s[i] %= MOD;
        }
    }
    f[0] = 1;
    for(int i=1;i<=n;i++)
        for(int j=i;j>=1;j--)
            f[j] = (f[j] + f[j-1] * s[i] % MOD) % MOD;
    for(int i=1;i<=n;i++) tot_meal += f[i] ,tot_meal %= MOD;
    //cout << tot_meal << endl;
    LL ans = tot_meal;
    for(register int cur=1;cur<=m;cur++)
    {
        memset(dp,0,sizeof(dp));
        dp[0][n] = 1;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n+i;j++)
            {
                dp[i][j] = dp[i-1][j] % MOD;
                (dp[i][j] += dp[i-1][j+1] * (s[i] - a[i][cur])) %= MOD;
                (dp[i][j] += dp[i-1][j-1] * a[i][cur]) %= MOD;
            }
        }
        for(int j=n+1;j<=2*n;j++) ans = (ans - dp[n][j] + MOD) % MOD;
    }
    cout << ans << endl;
}

 

上一篇:CSP2019题解


下一篇:CSP2019游记