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