原題傳送門
每行最多取一個告訴我們可以枚舉行
所以這道題目總體複雜度裏肯定有行的複雜度
考場上寫的是的暴力,直接把每一列分別取了幾個寫到狀態裏面去
正解需要考慮正難則反
我們寫直接將每行最多一個當作大前提
要求的是沒有一列取了超過一半的東西
那麼這個答案其實 =所有方案-有一列取了超過一半的方案
這裏就有一個重要性質:最多隻有一列會超過一半(雖然顯然但是我考場上並沒發現)
令
表示第行的和
表示前行選了個的方案數
表示前行,超過一半的列比其他所有多個的方案數
先求
再求
我這個狀態的設計已經將超過一半的情況表示了出來,但是並沒有表示超過的一半的列是哪一列
但是我們可以枚舉超過一半的列,再具體求,因爲枚舉的複雜度是,求值的複雜度是,所以,求數組的值爲本題主要複雜度,
令第列選擇的東西超過了一半
答案爲
Code:
#include <bits/stdc++.h>
#define maxn 4010
#define LL long long
using namespace std;
const LL qy = 998244353;
int n, m;
LL ans, s[maxn], f[110][maxn], g[maxn][maxn], a[maxn][maxn];
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
int main(){
n = read(), m = read();
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) (s[i] += a[i][j] = read()) %= qy;
for (int k = 1; k <= m; ++k){
memset(f, 0, sizeof(f));
f[0][n] = 1;
for (int i = 1; i <= n; ++i)
for (int j = n - i; j <= n + i; ++j) f[i][j] = (f[i - 1][j] + a[i][k] * f[i - 1][j - 1] % qy + (s[i] - a[i][k] + qy) % qy * f[i - 1][j + 1] % qy) % qy;
for (int i = 1; i <= n; ++i) (ans += f[n][n + i]) % qy;
}
g[0][0] = 1;
for (int i = 1; i <= n; ++i)
for (int j = 0; j <= n; ++j) g[i][j] = (g[i - 1][j] + (j ? s[i] * g[i - 1][j - 1] % qy : 0)) % qy;
ans = (-ans + qy) % qy;
for (int i = 1; i <= n; ++i) (ans += g[n][i]) %= qy;
printf("%lld\n", ans);
return 0;
}