NOIP2019 Emiya家今天的饭
ACM退役选手远程口胡
csf如今真的是太菜了,最后16分的做法愣是想了一下午
考虑使用容斥方法:
1
采用动态规划,先求出在无限制情况下,安排种烹饪方法总的方案数.
记表示已经考虑完前种烹饪方法,共做了个菜的方案数.
那么显然,决策分2种情况,用或不用第种烹饪方法,用的话就只能选一种主要食材.
时间复杂度,可以提前维护好.
2
采用动态规划,计算出那些不合法的方案,并将这些方案减掉.
因为每次只能有一个主要食材不合法.所以对每个主要食材单独考虑,假设当前食材不合法了.
最朴素的想法是,采用表示考虑完前种烹饪方法,已经做了个菜,使用食材的有个的方案数.
那么,决策就是第中烹饪方案选不选,选了之后,选不选作为食材,一共个转移.
记录
最后答案减去
时间复杂度为,只能过84分,接下来继续优化
事实上,我们无需同时记录和,而只需要记录的值就足够了,也就是食材的数量和非食材的数量.
这样的话,记录表示考虑完前行,选取的食材和非食材的差值为时候,方案数.
转移方程如下
最后答案减去
时间复杂度
综上,总的时间复杂度
AC代码
#include <iostream>
#include <cstring>
using namespace std;
#define int long long
const int MOD = 998244353;
const int maxn = 107,maxm = 2007;
int dp1[maxn][2*maxn]; //
int dp2[maxn][maxn];
int linesum[maxn]; //s1表示
int a[maxn][maxm];
int n, m;
signed main() {
cin >> n >> m;
for(int i = 1;i <= n;++i) {
for(int j = 1;j <= m;++j) {
cin >> a[i][j];
a[i][j] %= MOD;
linesum[i] = ( linesum[i] + a[i][j] ) % MOD;
}
}
int ans = 0;
for(int t = 1;t <= m;++t) {
memset(dp1,0,sizeof(dp1));
dp1[0][0 + 100] = 1;
for(int i = 1;i <= n;++i) {
for(int j = -i + 100;j <= i + 100;++j) {
dp1[i][j] = dp1[i-1][j];
dp1[i][j] += (dp1[i-1][j-1] * a[i][t]) % MOD;//取
dp1[i][j] += (dp1[i-1][j+1] * ((linesum[i] - a[i][t] + MOD) % MOD)) % MOD;//不取
dp1[i][j] %= MOD;
}
}
for(int j = 1;j <= n;++j) {
ans = (ans - dp1[n][j + 100]) % MOD;
}
}
dp2[0][0] = 1;
for(int i = 1;i <= n;++i) {
for(int j = 0;j <= i;++j) {
dp2[i][j] = dp2[i-1][j];
for(int k = 1;k <= m;++k) {
dp2[i][j] += dp2[i-1][j-1] * a[i][k] % MOD;
dp2[i][j] %= MOD;
}
}
}
for(int j = 1;j <= n;++j)
ans = (ans + dp2[n][j]) % MOD;
cout << ans << endl;
return 0;
}