【題解】LuoGu5664:Emiya 家今天的飯

原題傳送門
每行最多取一個告訴我們可以枚舉行
所以這道題目總體複雜度裏肯定有行的複雜度O(n)O(n)
考場上寫的是m=2/3m=2/3的暴力,直接把每一列分別取了幾個寫到狀態裏面去

正解需要考慮正難則反
我們寫直接將每行最多一個當作大前提
要求的是沒有一列取了超過一半的東西
那麼這個答案其實 =所有方案-有一列取了超過一半的方案
這裏就有一個重要性質:最多隻有一列會超過一半(雖然顯然但是我考場上並沒發現)


sis_i表示第ii行的和
gi,jg_{i,j}表示前ii行選了jj個的方案數
fi,jf_{i,j}表示前ii行,超過一半的列比其他所有多jj個的方案數

先求gi,jg_{i,j}
gi,j=gi1,j+sigi1,j1g_{i,j}=g_{i-1,j}+s_i*g_{i-1,j-1}
再求fi,jf_{i,j}
我這個狀態的設計已經將超過一半的情況表示了出來,但是並沒有表示超過的一半的列是哪一列
但是我們可以枚舉超過一半的列,再具體求ff,因爲枚舉的複雜度是O(m)O(m),求值的複雜度是O(nm)O(nm),所以,求ff數組的值爲本題主要複雜度,O(n2m)O(n^2m)
令第kk列選擇的東西超過了一半
fi,j=fi1,j+ai,kfi1,j1+(siai,k)fi1,j+1f_{i,j}=f_{i-1,j}+a_{i,k}*f_{i-1,j-1}+(s_i-a_{i,k})*f_{i-1,j+1}
答案爲i=1nfn,i\sum_{i=1}^{n}f_{n,i}

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章