1. 程式人生 > >[PKUSC2018]最大前綴和(DP)

[PKUSC2018]最大前綴和(DP)

集合 大於 怎麽 amp font turn tdi ont 序列

題意:求一個序列隨機打亂後最大前綴和的期望。

考場上發現不管怎麽設狀態都寫不出來,實際上只要稍微轉換一下就好了。

一個前綴[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 }

[PKUSC2018]最大前綴和(DP)