PKUSC 2018 最大字首和
阿新 • • 發佈:2019-01-06
題意
給出一個序列,把他隨機打亂,然後求最大字首和的期望
n<=20
題解
定義f(S)為S為最大字首和的方案數,g(S)為S中的元素,最大字首和<0的方案數,sum(S)為S中元素的和
那麼答案就是(設T為全集)
下面就是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);
}