[PKUSC2018] 最大前缀和

\(\text{Problem}:\)[PKUSC2018] 最大前缀和

\(\text{Solution}:\)

不难发现,任意一个序列都可以表示为两个有着不同特殊性质序列的拼接,记为 \(A+B\)\(A\)\(B\) 可以为空),有:

  • 序列 \(A\) 的性质:最大前缀和等于总和。
  • 序列 \(B\) 的性质:所有前缀和小于 \(0\)

\(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;
}
posted @ 2021-05-03 09:04  zkdxl  阅读(96)  评论(5编辑  收藏  举报