题目链接: P5664 [CSP-S2019] Emiya 家今天的饭
思路:
显然可以算出总数减去不合法的,不合法即有一列超过一半,显然最多一列,枚举这一列。
考虑 dp,设 \(f(i,j,k)\) 表示前 \(i\) 个方法,\(j\) 个这一列,\(k\) 个其他列。
但是这样是 \(O(n^3m)\),我们需要优化。
显然我们只关心 \(j,k\) 相对大小,直接差值 dp 即可。
点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 105;
const int M = 2005;
const int mod = 998244353;
int n, m;
int a[N][M] = {{0}};
int s[N] = {0};
void add(int &x, int y) {
x = (x + y) % mod;
}
int f[N][N * 2] = {{0}};
int cal(int x) {
f[0][n] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= n * 2; j++) {
f[i][j] = 0;
add(f[i][j], f[i - 1][j]);
if (j > 0)
add(f[i][j], 1ll * a[i][x] * f[i - 1][j - 1] % mod);
if (j < n * 2)
add(f[i][j], 1ll * (s[i] - a[i][x] + mod) % mod * f[i - 1][j + 1] % mod);
}
}
int ans = 0;
for (int i = n + 1; i <= n * 2; i++)
add(ans, f[n][i]);
return ans;
}
int main() {
cin >> n >> m;
int ans = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
add(s[i], a[i][j]);
}
ans = 1ll * ans * (s[i] + 1) % mod;
}
ans = (ans - 1 + mod) % mod;
for (int j = 1; j <= m; j++)
ans = (ans - cal(j) + mod) % mod;
cout << ans << endl;
return 0;
}