码迷,mamicode.com
首页 > 其他好文 > 详细

P5369 [PKUSC2018]最大前缀和

时间:2019-10-06 00:04:27      阅读:85      评论:0      收藏:0      [点我收藏+]

标签:using   方案   负数   ref   sum   ret   int   ios   排列   

状态压缩

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

P5369 [PKUSC2018]最大前缀和

标签:using   方案   负数   ref   sum   ret   int   ios   排列   

原文地址:https://www.cnblogs.com/Hs-black/p/11626107.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!