考虑去枚举这个构成最大前缀和的集合$S$,那么限制分为两条:
1.前$|S|$个元素都属于$S$,且任意非空前缀和都小于$sum(S)$(其中$sum(S)$表示$S$中元素之和)(这里的小于是避免统计重复,强制要求最长的前缀)
2.后$n-|S|$个元素都不属于$S$,且最大前缀和为0(允许为空)
令$f(S)$表示$S$中的元素最大前缀和为0(允许为空)的方案数,这个的必要条件为$sum(S)le 0$,同时在这样的条件下,对于序列最后一个数是无关的,因此$f(S)=sum_{xin S}f(S-x)$(这里$S-x$指去除$x$)
对于第一个条件,也就是最小非空后缀和大于0,将$a_{i}$变为其相反数后,也就是最大非空后缀和小于0,但特别的,这个后缀不能等于全集(因为本来的前缀非空)
先不考虑不能等于全集的条件,取反后用类似的方式求出$g(S)$(后缀和前缀是等价的),那么接下来再任意填上最后一个数即可,即$g(S)=sum_{xin S}g(S-x)$(类似背包的原理,要从后往前做)
由此即可$o(n2^{n})$求出答案
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 2000005 4 #define mod 998244353 5 int n,ans,sum[N],g[N],f[N]; 6 int main(){ 7 scanf("%d",&n); 8 for(int i=0;i<n;i++)scanf("%d",&sum[1<<i]); 9 g[0]=f[0]=1;//g[S]表示S中所有数最小前缀和为0 10 for(int i=1;i<(1<<n);i++){ 11 int k=(i&(i-1)); 12 sum[i]=sum[k]+sum[i^k]; 13 if (sum[i]>0){ 14 for(int j=0;j<n;j++) 15 if (i&(1<<j))g[i]=(g[i]+g[i^(1<<j)])%mod; 16 } 17 if (sum[i]<=0){ 18 for(int j=0;j<n;j++) 19 if (i&(1<<j))f[i]=(f[i]+f[i^(1<<j)])%mod; 20 } 21 } 22 for(int i=(1<<n)-1;i;i--) 23 if ((i&(i-1))==0)g[i]=1; 24 else{ 25 g[i]=0; 26 for(int j=0;j<n;j++) 27 if (i&(1<<j))g[i]=(g[i]+g[i^(1<<j)])%mod; 28 } 29 for(int i=0;i<(1<<n);i++)ans=(ans+1LL*(sum[i]+mod)*g[i]%mod*f[(1<<n)-1-i])%mod; 30 printf("%d",ans); 31 }