1. 程式人生 > >PKUSC 2018 最大字首和

PKUSC 2018 最大字首和

題意

給出一個序列,把他隨機打亂,然後求最大字首和的期望
n<=20

題解

定義f(S)為S為最大字首和的方案數,g(S)為S中的元素,最大字首和<0的方案數,sum(S)為S中元素的和
那麼答案就是(設T為全集)
S = 1

T f [ S ] g [ T x
o r S ] s u m [ S ]
\sum_{S=1}^Tf[S]g[TxorS]sum[S]
下面就是f和g怎麼求了
比如對於S,我們找到一個不在其中的元素u

那麼f[S∪u]就可以看做在S前面放上一個u,那麼這個轉移能成立必須當sum[S]>=0(此處,我們不需要也不能考慮sum[S]+val[u]的正負)

g[S∪u]可以看做在S後面放上一個u,那麼這個轉移能成立,必須當sum[S]+val[u]<0,(為什麼不能等於0?是因為會和上面那個f算重了)

可以想見,(其實是隻會口胡),這樣的轉移是沒有遺漏也沒有重複的

於是就醬了。以上。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=22,M=1048580;
const int mod=998244353;
int f[M],g[M];
int n;
int a[N];
int sum[M];
int T;
int bits[M];
inline int lowbit(int x){
    return x&(-x);
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
        bits[1<<i]=i;
    }
    T=(1<<n)-1;
    for(int S=0;S<=T;S++){
        int U=S^T;
        while(U){
            int u=lowbit(U);
            U^=u;
            sum[S|u]=sum[S]+a[bits[u]];
        }
    }
    f[0]=1;
    for(int S=0;S<=T;S++){
        if(sum[S]<0)
            continue ;
        int U=S^T;
        while(U){
            int u=lowbit(U);
            U^=u;
            f[S|u]=(f[S|u]+f[S])%mod;
        }
    }
    g[0]=1;
    for(int S=0;S<=T;S++){
        int U=S^T;
        while(U){
            int u=lowbit(U);
            U^=u;
            if(sum[S]+a[bits[u]]<0)
                g[S|u]=(g[S|u]+g[S])%mod;
        }
    }
    int ans=0;
    for(int S=1;S<=T;S++){
        ans+=1ll*f[S]*g[T^S]%mod*sum[S]%mod;
        ans%=mod;
    }
    ans=(ans+mod)%mod;
    printf("%d\n",ans);
}