1. 程式人生 > >「PKUSC2018」最大字首和 [DP?]

「PKUSC2018」最大字首和 [DP?]

「PKUSC2018」最大字首和

Tags: DP 狀壓


「PKUSC2018」最大字首和

題意

求對於a[]的所有排列的的最大字首和的和對998244353取模的值。

分析

其實就是一個計數問題?和概率期望沒有什麼關係。
然後考慮狀壓,然後某個狀態表示的是選擇了當前那麼多東西之後的最大字首和之和。
考慮選擇或者不選擇後面的一段?
但是這樣還是有點問題。

只需要考慮如果當前的s被作為最大的字首和的情況數量。
那麼其實考慮有多少個剩下元素組成的最大字首和<0

記dp1為以s為最大字首和的串的個數。(前面沒有比它更大的)
記dp2為s有多少種取法使得最大字首和<0

對於dp1

d p 1 [ n s
] = s , s | ( 1 << i
) = n s
d p 1 [ s ] ( i f s u m [ n s ] > 0 ) + 1 ( i f s = 0 )

對於dp2
d p 2 [ n s ] = { s , s | ( 1 << i ) = n s d p 2 [ s ] , ( i f s u m [ s ] 0 ) 0 , ( i f s u m [ s ] > 0 )
然後答案就等於
d p 1 [ s ] d p 2 [ t m p x o r s ] s u m [ s ]
大概好了。…
挺簡單的呀?
為什麼考場上想不出來呢?qwq

code

#include<bits/stdc++.h>
#define M 25
#define mo 998244353
using namespace std;
void read(int &x){
    x=0; char c=getchar(); int p=1;
    for (;c<48;c=getchar())if (c=='-')p=-1;
    for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
    x*=p;
}
#define lowbit(p) (p&(-p))
int a[M],sum[1<<20],dp[1<<20][2];
void add(int &x,int y){
    x=(x+y)%mo;
}
int main(){
//  freopen("1.in","r",stdin);
    int n,i,tmp,res=0,j;
    read(n);
    for (i=0;i<n;i++){
        read(a[i]);
        sum[1<<i]+=a[i];
        dp[1<<i][0]=1;
    }   
    tmp=1<<n;
    for (i=1;i<tmp;i++){
        sum[i]=sum[i^(lowbit(i))]+sum[lowbit(i)];
    }
    dp[0][0]=dp[0][1]=1;
    for (i=0;i<tmp;i++){
        for (j=0;j<n;j++)if (!(i&(1<<j))){
            if(sum[i]>0)add(dp[i|(1<<j)][0],dp[i][0]);
            if(sum[i|(1<<j)]<=0)add(dp[i|(1<<j)][1],dp[i][1]);
        }
    }
    for (i=1;i<tmp;i++){
        add(res,1ll*sum[i]*dp[i][0]%mo*dp[(tmp-1)^i][1]%mo);
    }
    printf("%d\n",(res+mo)%mo);
    return 0;
}