【LOJ6433】【PKUSC2018】最大前缀和

【LOJ6433】【PKUSC2018】最大前缀和

题面

题目描述

小 C 是一个算法竞赛爱好者,有一天小 C 遇到了一个非常难的问题:求一个序列的最大子段和。

但是小 C 并不会做这个题,于是小 C 决定把序列随机打乱,然后取序列的最大前缀和作为答案。

小 C 是一个非常有自知之明的人,他知道自己的算法完全不对,所以并不关心正确率,他只关心求出的解的期望值,现在请你帮他解决这个问题,由于答案可能非常复杂,所以你只需要输出答案乘上 \(n!\) 后对 \(998244353\) 取模的值,显然这是个整数。

注:最大前缀和的定义:\(\forall i \in [1,n]\)\(\sum_{j=1}^{i}a_j\) 的最大值。

输入格式

第一行一个正整数 \(n\),表示序列长度。

第二行 \(n\)个数,表示原序列 \(a[1..n]\),第 \(i\) 个数表示 \(a[i]\)

输出格式

输出一个非负整数,表示答案。

样例

输入样例

2
-1 2

输出样例

3

提示与说明

对于\(10\%\)的数据,有\(1\leq n\leq 9\)

对于\(40\%\)的数据,有\(1\leq n\leq 15\)

另有\(10\%\)的数据,满足\(a\)中最多只有一个负数。

另有\(10\%\)的数据,满足\(|a[i]|\leq 2\)

对于\(100\%\)的数据,满足\(1\leq n\leq 20\)\(\sum_{i=1}^{n}|a[i]|\leq 10^9\)

题解

因为假如有\(\forall i\sum_{j=1}^{p-1} a_i\),且\(\exists \sum_{j=p}^na_j \geq 0\)

最优解肯定不在\(1-p-1\)中,所以

预处理几个东西(i是子集):

\(sum[i]:\)状态为\(i\)时所有数的和

\(f[i]:\)状态为\(i\)时和为\(sum[i]\)的方案数

\(g[i]:\)状态为\(i\)时排列为正的方案数

转移看代码

压行真舒服

代码

#include <iostream> 
#include <cstdio> 
using namespace std;
#define rep(i, from, to) for(int i = (from); i <= (to); i++) 
typedef long long ll; 
const int Mod = 998244353; 
int N, a[20], f[1 << 20], g[1 << 20]; 
ll sum[1 << 20];
void pls(int &x, int y) { x += y; if (x >= Mod) x -= Mod; } 
int main () {
	cin >> N; rep(i, 0, N - 1) cin >> a[i]; 
	int S = (1 << N) - 1; 
	g[0] = 1; 
	rep (i, 0, N - 1) { f[1 << i] = 1; rep(s, 0, S) if (s & (1 << i)) sum[s] += a[i]; } 
	rep (s, 0, S) { 
		if (sum[s] > 0) rep (i, 0, N - 1) { if ((s & (1 << i)) == 0) pls(f[s | (1 << i)], f[s]); }
		else rep (i, 0, N - 1) if (s & (1 << i)) pls(g[s], g[s ^ (1 << i)]); 
	} 
	rep (s, 0, S) { sum[s] %= Mod; while (sum[s] < 0) sum[s] += Mod; }
	int ans = 0; rep (s, 0, S) pls(ans, 1ll * sum[s] * f[s] % Mod * g[S ^ s] % Mod); 
	printf("%d\n", ans); 
	return 0; 
} 
posted @ 2019-01-18 17:34  heyujun  阅读(535)  评论(0编辑  收藏  举报