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