PKUSC 2018 最大前缀和

版权声明:本文为博主原创文章,未经博主允许也可以转载。 https://blog.csdn.net/qq_35541672/article/details/85236472

题意

给出一个序列,把他随机打乱,然后求最大前缀和的期望
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);
}

猜你喜欢

转载自blog.csdn.net/qq_35541672/article/details/85236472