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

相關文章
相關標籤/搜索