原题传送门
这种题目真有意思,总是披着期望的外套,然后还不忘说一声答案乘一个xxx,可以证明是整数
这不就是方案和吗,这样子又不是会有人看不出来
求全排列的最大前缀和之和
首先可以发现一个重要性质,对于一个排列,若最大前缀和的结尾位置在,可以发现
根据这个性质可以状压dp
令
表示状态的所有数之和
表示状态的排列最大前缀和为的方案数
表示状态的排列所有前缀和都<=0的方案数
可以得到答案
通过dp得到
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;
}