NOIP2019 Emiya家今天的飯

NOIP2019 Emiya家今天的飯

ACM退役選手遠程口胡
csf如今真的是太菜了,最後16分的做法愣是想了一下午
考慮使用容斥方法:

1

採用動態規劃,先求出在無限制情況下,安排kk種烹飪方法總的方案數.
dp2[i][j]dp2[i][j]表示已經考慮完前ii種烹飪方法,共做了jj個菜的方案數.
那麼顯然,決策分2種情況,用或不用第ii種烹飪方法,用的話就只能選一種主要食材.
dp2[i][j]=dp2[i1][j]+dp2[i1][j1](t=1ma[i][t])dp2[i][j]=dp2[i-1][j] + dp2[i-1][j-1]*(\sum_{t=1}^m a[i][t])
時間複雜度O(n2)O(n^2),t=1ma[i][t]\sum_{t=1}^m a[i][t]可以提前維護好.

2

採用動態規劃,計算出那些不合法的方案,並將這些方案減掉.
因爲每次只能有一個主要食材不合法.所以對每個主要食材單獨考慮,假設當前tt食材不合法了.
最樸素的想法是,採用dp1[i][j][k]dp1[i][j][k]表示考慮完前ii烹飪方法,已經做了jj個菜,使用tt食材的有kk個的方案數.

那麼,決策就是第ii中烹飪方案選不選,選了之後,選不選tt作爲食材,一共33個轉移.

記錄linesum[i]=t=1ma[i][t]linesum[i]=\sum_{t=1}^m a[i][t]

dp1[i][j][k]=dp1[i1][j][k]+dp1[i1][j1][k1]a[i][t]+dp1[i1][j1][k](linesum[i]a[i][t])dp1[i][j][k]=dp1[i-1][j][k]+dp1[i-1][j-1][k-1]*a[i][t]+dp1[i-1][j-1][k]*(linesum[i]-a[i][t])

最後答案減去dp1[n][j][k]k>j/2dp1[n][j][k]|_{k \gt j/2}

時間複雜度爲O(n3m)O(n^3m),只能過84分,接下來繼續優化

事實上,我們無需同時記錄jjkk,而只需要記錄k(jk)k - (j-k)的值就足夠了,也就是tt食材的數量和非tt食材的數量.

這樣的話,記錄dp1[i][Δ]dp1[i][\Delta]表示考慮完前ii行,選取的tt食材和非tt食材的差值爲Δ\Delta時候,方案數.
轉移方程如下
dp1[i][Δ]=dp1[i1][Δ]+dp1[i1][Δ1]a[i][t]+dp1[i1][Δ+1](linesum[i]a[i][t])dp1[i][\Delta]=dp1[i-1][\Delta] + dp1[i-1][\Delta-1]*a[i][t]+dp1[i-1][\Delta+1]*(linesum[i]-a[i][t])
最後答案減去dp1[n][Δ]Δ>0dp1[n][\Delta]|_{\Delta>0}
時間複雜度O(n2m)O(n^2m)

綜上,總的時間複雜度O(n2m)O(n^2m)

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