【題解】LuoGu5369:[PKUSC2018]最大前綴和

原題傳送門
這種題目真有意思,總是披着期望的外套,然後還不忘說一聲答案乘一個xxx,可以證明是整數
這不就是方案和嗎,這樣子又不是會有人看不出來

求全排列的最大前綴和之和
首先可以發現一個重要性質,對於一個排列,若最大前綴和的結尾位置在pp,可以發現
i=xpai>0(x<=p)\sum_{i=x}^{p}a_i>0(x<=p)
i=p+1xai<=0(x>p)\sum_{i=p+1}^{x}a_i<=0(x>p)
根據這個性質可以狀壓dp

sumisum_i表示狀態ii的所有數之和
fif_i表示狀態ii的排列最大前綴和爲sumisum_i的方案數
gig_i表示狀態ii的排列所有前綴和都<=0的方案數
可以得到答案
ans=i=02n1sumifig2n1ians=\sum_{i=0}^{2^n-1}sum_i*f_i*g_{2^n-1-i}
通過dp得到f,gf,g
gi+=gi2j(sumi<=02ji)g_i+=g_{i-2_j}(sum_i<=0\text{且}2^j∈i)
fi+2j+=fi(sumi>02ji)f_{i+2^j}+=f_i(sum_i>0\text{且}2_j∉i)

Code:

#include <bits/stdc++.h>
#define maxn 1200010
#define LL long long
using namespace std;
const LL qy = 998244353;
int n, N, power[25], num[maxn];
LL g[maxn], f[maxn], sum[maxn], ans;

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 lowbit(int x){ return x & -x; }
void upd(LL &x, LL y){ if ((x += y) >= qy) x -= qy; }

int main(){
	n = read(), N = (1 << n) - 1;
	power[0] = 1;
	for (int i = 1; i <= 20; ++i) power[i] = power[i - 1] << 1;
	for (int i = 1; i <= n; ++i) num[power[i - 1]] = read();
	for (int i = 1; i <= N; ++i) upd(sum[i], sum[i ^ lowbit(i)] + num[lowbit(i)]);
	g[0] = 1;
	for (int i = 0; i <= N; ++i)
	if (sum[i] <= 0)
		for (int j = 0; j < n; ++j)
			if (i & power[j]) upd(g[i], g[i ^ power[j]]);
	for (int i = 0; i < n; ++i) f[power[i]] = 1;
	for (int i = 0; i <= N; ++i){
		upd(ans, (sum[i] + qy) % qy * f[i] % qy * g[N ^ i] % qy);
		if (sum[i] > 0)
			for (int j = 0; j < n; ++j)
				if (!(i & power[j])) upd(f[i ^ power[j]], f[i]);
	}
	printf("%lld\n", ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章