LOJ传送门
题目描述
小 C 是一个算法竞赛爱好者,有一天小 C 遇到了一个非常难的问题:求一个序列的最大子段和。
但是小 C 并不会做这个题,于是小 C 决定把序列随机打乱,然后取序列的最大前缀和作为答案。
小 C 是一个非常有自知之明的人,他知道自己的算法完全不对,所以并不关心正确率,他只关心求出的解的期望值,现在请你帮他解决这个问题,由于答案可能非常复杂,所以你只需要输出答案乘上 n ! n! n! 后对 998244353 998244353 998244353 取模的值,显然这是个整数。
注:最大前缀和的定义: ∀ i ∈ [ 1 , n ] \forall i \in [1,n] ∀i∈[1,n], ∑ j = 1 i \sum_{j=1}^{i} ∑j=1i的最大值。
输入输出格式
输入格式
第一行一个正整数 n n n,表示序列长度。
第二行 n n n 个数,表示原序列 a [ 1.. n ] a[1..n] a[1..n],第 i i i 个数表示 $a[i] $。
输出格式
输出一个非负整数,表示答案。
输入输出样例
输入样例#1
2
-1 2
输出样例#1
3
数据范围与提示
数据范围
对于 10 % 10\% 10%的数据,有 1 ≤ n ≤ 9 1\leq n\leq 9 1≤n≤9。
对于 40 % 40\% 40%的数据,有 1 ≤ n ≤ 15 1\leq n\leq 15 1≤n≤15。
另有 10 % 10\% 10%的数据,满足 a a a中最多只有一个负数。
另有 10 % 10\% 10%的数据,满足 ∣ a [ i ] ∣ ≤ 2 |a[i]|\leq 2 ∣a[i]∣≤2。
对于 100 % 100\% 100%的数据,满足 1 ≤ n ≤ 20 1\leq n\leq 20 1≤n≤20, ∑ i = 1 n ∣ a [ i ] ∣ ≤ 1 0 9 \sum_{i=1}^{n}|a[i]|\leq 10^9 ∑i=1n∣a[i]∣≤109。
解题分析
在 P K U W C PKUWC PKUWC做的试机题…
直接考虑排列是行不通的, 考虑一个位置 p p p, 我们什么时候会在这个位置取到最大前缀和:对于 ∀ i ∈ [ 1 , p ] \forall i\in [1,p] ∀i∈[1,p]有 ∑ i p a [ i ] ≥ 0 \sum_{i}^{p}a[i]\ge 0 ∑ipa[i]≥0, 对于 ∀ i ∈ [ p + 1 , n ] \forall i\in[p+1,n] ∀i∈[p+1,n]有 ∑ i n a [ i ] < 0 \sum_i^{n}a[i]< 0 ∑ina[i]<0。 这样直接分开 d p dp dp, 最后答案是满足要求的前缀和*前缀排列方案数*后缀排列方案数。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <cctype>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MOD 998244353
#define ll long long
template <class T>
IN void in(T &x)
{
static char c; static bool neg;
x = 0; c = gc;
for (; !isdigit(c); c = gc)
if (c == '-') neg = true;
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
if (neg) neg = false, x = -x;
}
IN void ad(int &ad, R int add) {ad += add; if (ad > MOD) ad -= MOD;}
const int MX = 21;
const int SIZ = 1 << 20;
int n;
int val[MX], f[SIZ], g[SIZ], sum[SIZ];
int main(void)
{
R int i, j, foo, all;
in(n); g[0] = 1; all = (1 << n) - 1;
for (i = 0; i < n; ++i) in(val[i]), f[1 << i] = 1;
for (i = 1; i <= all; ++i)
{
foo = i;
W (foo)
{
j = __builtin_ctz(foo);
ad(sum[i], val[j]);
foo ^= (1 << j);
}
}
for (R int i = 1; i <= all; ++i)
{
if (sum[i] > 0)
{
for (j = 0; j < n; ++j)
if (!(i & (1 << j))) ad(f[i | (1 << j)], f[i]);
}
else
{
for (j = 0; j < n; ++j)
if (i & (1 << j)) ad(g[i], g[i ^ (1 << j)]);
}
}
int ans = 0;
for (R int i = 1; i <= all; ++i)
ad(ans, 1ll * f[i] * g[all ^ i] % MOD * ((sum[i] + MOD) % MOD) % MOD);
printf("%d", ans);
}