[LOJ 6433][PKUSC 2018]最大前缀和

[LOJ 6433][PKUSC 2018]最大前缀和

题意

给定一个长度为 \(n\) 的序列, 求把这个序列随机打乱后的最大前缀和的指望乘以 \(n!\) 后对 \(998244353\) 取膜后的值.c++

前缀和不能为空.spa

\(n\le 20\).code

题解

首先这个指望显然是逗你玩的...只是计数而已blog

而后咱们把一个序列拆成两部分, 一部分前缀和都不大于总和, 一部分前缀和都不大于 \(0\). 那么显然这样的一个序列的最大前缀和就是第一部分的和. 咱们只要知道有多少个这样的序列就行了.get

后面的作法感受有点意思it

咱们用两个DP分别求解序列的某个子集组成两个部分的方案数量. 若是当前集合的和大于 \(0\) 那么显然不能用来组成第二部分, 可是咱们能够在这个集合产生的合法第一部分的前面加一个值来组成新的第一部分. 而若是当前集合的和不大于 \(0\), 那么它只能用于构成第二部分, 并且咱们能够判定若是在这个集合中钦定某个值放在最后, 那么只要剩下的值能构成合法的第二部分, 新的序列也能构成合法的第二部分.class

最后枚举那些值在第一部分, 剩下值丢给第二部分, 卷起来就能够了.im

参考代码

#include <bits/stdc++.h>

const int MAXN=21;
const int MOD=998244353;
const int MAXL=(1<<20)|3;

int n;
int a[MAXN];
int dp1[MAXL];
int dp2[MAXL];
int sum[MAXL];

inline int LowBit(int);

int main(){
    scanf("%d",&n);
    dp2[0]=1;
    for(int i=0;i<n;i++){
        scanf("%d",a+i);
        dp1[1<<i]=1;
        sum[1<<i]=a[i];
    }
    for(int s=1;s<(1<<n);s++){
        if(s!=LowBit(s))
            sum[s]=sum[s^LowBit(s)]+sum[LowBit(s)];
        if(sum[s]>0){
            for(int i=0;i<n;i++)
                if((s&(1<<i))==0)
                    (dp1[s^(1<<i)]+=dp1[s])%=MOD;
        }
        else{
            for(int i=0;i<n;i++)
                if((s&(1<<i))!=0)
                    (dp2[s]+=dp2[s^(1<<i)])%=MOD;
        }
    }
    int ans=0;
    for(int s=1;s<(1<<n);s++)
        (ans+=1ll*sum[s]*dp1[s]%MOD*dp2[((1<<n)-1)^s]%MOD)%=MOD;
    printf("%d\n",ans<0?ans+MOD:ans);
    return 0;
}

inline int LowBit(int x){
    return x&-x;
}

相关文章
相关标签/搜索