考虑对于一个序列,如果 Sp S p 是最大前缀和,那么对于p+1…n这个序列,需要满足任意前缀和均<=0.否则 Sp S p 一定不是最大前缀和。
sum[s]表示s子集的点的权值和。 f[s],表示选了s的点,sum[s]为最大前缀和的方案数 g[s],表示选了s的点,所有前缀和均<=0的方案数 那么最后答案就是 ∑sum[s]∗f[s]∗g[s¯¯¯] ∑ s u m [ s ] ∗ f [ s ] ∗ g [ s ¯ ] f[s]的转移,如果sum[s]>0,可以考虑在满足f[s]的方案最前面放一个没出现过的数,那么一定可以转移到f[s|bin[i]]。 g[s]的转移,可以考虑在后面再放一个i,如果sum[s|bin[i]]<0那么就可以转移到g[s|bin[i]| 复杂度 O(2nn) O ( 2 n n )
#include <bits/stdc++.h> using namespace std; #define ll long long #define inf 0x3f3f3f3f #define N 25 #define mod 998244353 inline char gc(){ static char buf[1<<16],*S,*T; if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;} return *S++; } inline int read(){ int x=0,f=1;char ch=gc(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc(); return x*f; } int n,a[N],sum[1100000],f[1100000],g[1100000],bin[N],ans=0; //f[s],选了s的点,sum[s]为最大前缀和的方案数 g[s],选了s的点,所有前缀和均<=0的方案数 inline void inc(int &x,int y){x+=y;x%=mod;} int main(){ // freopen("a.in","r",stdin); n=read();bin[0]=1;g[0]=1; for(int i=1;i<=n;++i) a[i]=read(),bin[i]=bin[i-1]<<1; for(int i=1;i<=n;++i){ f[bin[i-1]]=1; for(int s=0;s<bin[n];++s) if(s&bin[i-1]) inc(sum[s],a[i]); }for(int s=0;s<bin[n];++s){ if(sum[s]>0){ for(int i=0;i<n;++i) if(!(s&bin[i])) inc(f[s|bin[i]],f[s]);continue; }for(int i=0;i<n;++i) if(s&bin[i]) inc(g[s],g[s^bin[i]]); }for(int s=0;s<bin[n];++s) inc(ans,(ll)f[s]*sum[s]%mod*g[bin[n]-1^s]%mod); printf("%d\n",ans<0?ans+mod:ans); return 0; }