P5369 [PKUSC2018]最大前缀和

状态压缩

P5369

题意:求所有排列下的最大前缀和之和

一步转化: 求最大前缀和的前缀由数集S组成的方案数, 统计答案时直接乘上sum(S)即可

考虑最大前缀和的性质:

设最大前缀和为sum[i]

  1. 到i的后缀均为正数
  2. i后的前缀均为负数

令sum[i] = 集合 i 内所有数的和。

令f[i] = 集合 i内的数组成的排列,最大前缀和 = sum[i]的方案数。

令g[i] = 集合 i内的数组成的排列,所有的最大前缀和都 < 0 的方案数。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 25;
const int P = 998244353;
int n, a[N];
int f[1050050], g[1050050];
int sum[1050050];
inline int to(int x) {
	return 1 << x;
}
int main() {
	cin >> n; int all = to(n) - 1;
	for (int i = 1;i <= n; i++) 
	cin >> a[i], f[to(i-1)] = 1, sum[to(i-1)] = a[i];
	for (int i = 1;i <= all; i++) 
			sum[i] = sum[(i & -i)] + sum[i ^ (i & -i)];
	g[0] = 1;
	for (int i = 0;i < all; i++) {
		if (sum[i] >= 0) {
			for (int j = 1;j <= n; j++) 
				if (!(i & to(j-1))) 
					f[i | to(j-1)] = ((long long)f[i] + f[i | to(j-1)]) % P;
		}
		else {
			for (int j = 1;j <= n; j++) 
				if (i & (to(j-1))) 
					g[i] = ((long long)g[i] + g[i ^ to(j-1)]) % P;
		}
	}
	long long ans = 0;
	for (int i = 1;i <= all; i++)
		ans = (ans + (long long)f[i] * g[all^i] % P * sum[i] % P) % P;
	cout << (ans % P + P) % P << endl;
	return 0;
}
posted @ 2019-10-05 23:09  Hs-black  阅读(361)  评论(0编辑  收藏  举报