[PKUSC2018]最大前缀和(DP)

题意:求一个序列随机打乱后最大前缀和的期望。

考场上发现不管怎么设状态都写不出来,实际上只要稍微转换一下就好了。

一个前缀[1..k]是最大前缀,当且仅当前面的所有后缀[k-1,k],[k-2,k],...,[1,k]都大于0,后面的所有前缀[k+1,k+2],[k+1,k+3],...,[k+1,n]全部不大于0。

于是设f[S]表示S集合满足所有后缀大于0的排列数,g[S]表示S前缀不大于0的排列数,直接转移,最后答案就是所有集合(空集除外)的f乘上补集的g。

不卡时BZOJ时间榜与码长榜 rank 1。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l),_=(r); i<=_; i++)
 4 using namespace std;
 5 
 6 const int N=1048577,mod=998244353;
 7 int n,ans,a[N],cnt[N],f[N],g[N],ll[N];
 8 inline void up(int &x,int y){ x+=y; if (x>=mod) x-=mod; }
 9 
10 int main(){
11     scanf("%d",&n); int S=(1<<n)-1; g[0]=1;
12     rep(i,0,n-1) scanf("%d",&a[1<<i]);
13     rep(s,1,S){
14         int t=s&-s; cnt[s]=cnt[s^t]+a[t];
15         if (!(s^t)) { f[s]=1; g[s]=(a[t]<=0); continue; }
16         for (int p=s; p; p=p^t,t=p&-p){
17             if (cnt[s^t]>0) up(f[s],f[s^t]);
18             if (cnt[s]<=0) up(g[s],g[s^t]);
19         }
20     }
21     rep(s,1,S) ans=(ans+1ll*f[s]*g[S^s]%mod*cnt[s])%mod;
22     printf("%d\n",(ans+mod)%mod);
23     return 0;
24 }

猜你喜欢

转载自www.cnblogs.com/HocRiser/p/9162247.html