小C是一个算法竞赛爱好者,有一天小C遇到了一个非常难的问题:求一个序列的最大子段和。
但是小C并不会做这个题,于是小C决定把序列随机打乱,然后取序列的最大前缀和作为答案。
小C是一个非常有自知之明的人,他知道自己的算法完全不对,所以并不关心正确率,他只关心求出的解的期望值,
现在请你帮他解决这个问题,由于答案可能非常复杂,所以你只需要输出答案乘上n!后对998244353取模的值,显然这是个整数。
注:最大前缀和的定义:i∈[1,n],Sigma(aj)的最大值,其中1<=j<=i
第一行一个正整数nnn,表示序列长度。
第二行n个数,表示原序列a[1..n],第i个数表示a[i]。
1≤n≤20,Sigma(|Ai|)<=10^9,其中1<=i<=N
1 #include<iostream>
2 #include<cstdio>
3 #define N (21)
4 #define MOD (998244353)
5 using namespace std;
6
7 int n,m,a[N],sum[1<<N],cnt[1<<N],f[1<<N],g[1<<N];
8
9 int main()
10 {
11 scanf("%d",&n); m=(1<<n)-1;
12 for (int i=1; i<=n; ++i) scanf("%d",&a[i]);
13 for (int i=1; i<=n; ++i)
14 for (int S=0; S<=m; ++S)
15 if (S&(1<<i-1)) sum[S]+=a[i], cnt[S]++;
16
17 for (int S=1; S<=m; ++S)
18 {
19 if (cnt[S]==1) {f[S]=1; continue;}
20 for (int i=1; i<=n; ++i)
21 if ((S&(1<<i-1)) && sum[S]-a[i]>0)
22 (f[S]+=f[S^(1<<i-1)])%=MOD;
23 }
24
25 g[0]=1;
26 for (int S=1; S<=m; ++S)
27 {
28 if (sum[S]>0) {g[S]=0; continue;}
29 if (cnt[S]==1) {g[S]=1; continue;}
30 for (int i=1; i<=n; ++i)
31 if (S&(1<<i-1))
32 (g[S]+=g[S^(1<<i-1)])%=MOD;
33 }
34 int ans=0;
35 for (int S=1; S<=m; ++S)
36 (ans+=1ll*sum[S]*f[S]%MOD*g[m^S]%MOD)%=MOD;
37 ans=(ans%MOD+MOD)%MOD;
38 printf("%d\n",ans);
39 }