\(\text{Problem}:\)[PKUSC2018] 最大前缀和
\(\text{Solution}:\)
不难发现,任意一个序列都可以表示为两个有着不同特殊性质序列的拼接,记为 \(A+B\)(\(A\) 和 \(B\) 可以为空),有:
设 \(f_{S}\) 表示集合 \(S\) 中的数组成序列 \(A\) 的个数,\(g_{S}\) 表示集合 \(S\) 中的数组成序列 \(B\) 的个数,\(w_{S}\) 表示集合 \(S\) 中的数的总和,答案为:
\[\sum\limits_{i=0}^{2^{n}-1}w_{i}\times f_{i}\times g_{(2^{n}-1)\oplus i} \]考虑 \(dp\) 求出 \(f\) 和 \(g\)。\(g\) 的转移较为简单,难点在于 \(f\) 的转移。考虑转移过来的状态 \(T\),必须满足 \(w_{T}\geq 0\),否则不满足作为序列 \(A\) 的性质。故有:
\[f_{S}=\sum\limits_{i\in S}f_{S\oplus (1<<i)}[w_{S\oplus(1<<i)}\geq 0]\\ g_{S}=[w_{S}<0]\sum\limits_{i\in S}g_{S\oplus (1<<i)} \]总时间复杂度 \(O(2^{n}n)\)。
\(\text{Code}:\)
#include <bits/stdc++.h> #pragma GCC optimize(3) //#define int long long #define ri register #define mk make_pair #define fi first #define se second #define pb push_back #define eb emplace_back #define is insert #define es erase #define vi vector<int> #define vpi vector<pair<int,int>> using namespace std; const int N=(1<<20)+5, Mod=998244353; inline int read() { int s=0, w=1; ri char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); } while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar(); return s*w; } int n,m,a[N],F[N],G[N],W[N]; inline int lowbit(int x) { return x&(-x); } signed main() { n=read(), m=(1<<n); for(ri int i=0;i<n;i++) a[i]=W[1<<i]=read(); F[0]=G[0]=1; for(ri int i=1;i<m;i++) W[i]=W[i^lowbit(i)]+W[lowbit(i)]; for(ri int i=1;i<m;i++) { if(W[i]<0) { for(ri int j=0;j<n;j++) if((i>>j)&1) G[i]=(G[i]+G[i^(1<<j)])%Mod; } for(ri int j=0;j<n;j++) if(((i>>j)&1) && W[i^(1<<j)]>=0) F[i]=(F[i]+F[i^(1<<j)])%Mod; } int ans=0; for(ri int i=1;i<m;i++) ans=(ans+1ll*(W[i]%Mod+Mod)*F[i]%Mod*G[(m-1)^i]%Mod)%Mod; printf("%d\n",ans); return 0; }