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;
}

猜你喜欢

转载自www.cnblogs.com/Hs-black/p/11626107.html
今日推荐