- 上午的国庆大阅兵有意思
Description
Solution
看数据范围认解法
首先在每种情况出现概率相同的情况下, (期望 imes 方案数 = 权值和),即题意就是让你求所有排列的最大前缀和的总和……
我们可以枚举哪些数是最大前缀,显然这些数内部任意交换顺序、其它数内部任意交换顺序 都不会改变这个最大前缀。
一些数要排到前面去成为最大前缀,条件是该前缀除整段外的所有后缀和 (gt 0)(因为最大前缀长度不能是 (0)),后面的所有前缀和 (le 0)。
(一个 (gt 0),一个 (le 0) 是因为对于一种排列,若有多个前缀和均为最大,我们只根据最短的前缀统计一次该排序。也可以根据最长的前缀,即一个 (ge 0),一个 (lt 0))
设 (f(i)) 表示集合 (i) 的数有多少种排列满足所有后缀和 (gt 0),(g(i)) 表示集合 (i) 的数有多少种排列满足所有前缀和 (le 0)。
(f) 的转移是每次往前加一个数,(g) 的转移是每次往后加一个数。加一个数只需要判断一下新后/前缀和是否满足条件。
最后把 (f) 和 (g) 卷起来就好了。
#include<bits/stdc++.h>
#define ll long long
#define N 1048580
#define mod 998244353
using namespace std;
inline int read(){
int x=0; bool f=1; char c=getchar();
for(;!isdigit(c); c=getchar()) if(c=='-') f=0;
for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
if(f) return x; return 0-x;
}
int n,nn,f[N],g[N],ans; ll sum[N];
inline int lowbit(int x) {return x&-x;}
inline void upd(int &x, int y) {x = (x+y) % mod;}
int main(){
n=read(), nn=(1<<n)-1;
for(int i=0; i<n; ++i) sum[1<<i]=read();
for(int i=1; i<=nn; ++i){
int x=lowbit(i);
if(i^x) sum[i]=sum[i^x]+sum[x];
}
for(int i=0; i<n; ++i) f[1<<i]=1;
for(int i=1; i<=nn; ++i) if(sum[i]>0)
for(int j=0; j<n; ++j) if((i&(1<<j))==0)
upd(f[i^(1<<j)], f[i]);
//for(int i=0; i<=nn; ++i) cout<<f[i]<<' '; cout<<endl;
g[0]=1;
for(int i=0; i<=nn; ++i)
for(int j=0; j<n; ++j) if((i&(1<<j))==0 && sum[i^(1<<j)]<=0)
upd(g[i^(1<<j)], g[i]);
//for(int i=0; i<=nn; ++i) cout<<g[i]<<' '; cout<<endl;
for(int i=1; i<=nn; ++i)
upd(ans, (ll)f[i]*g[nn^i]%mod*((sum[i]%mod+mod)%mod)%mod);
cout<<ans<<endl;
return 0;
}