Written with StackEdit.

Description

\(C\)是一个算法竞赛爱好者,有一天小\(C\)遇到了一个非常难的问题:求一个序列的最大子段和。

但是小\(C\)并不会做这个题,于是小\(C\)决定把序列随机打乱,然后取序列的最大前缀和作为答案。

\(C\)是一个非常有自知之明的人,他知道自己的算法完全不对,所以并不关心正确率,他只关心求出的解的期望值,

现在请你帮他解决这个问题,由于答案可能非常复杂,所以你只需要输出答案乘上\(n!\)后对\(998244353\)取模的值,显然这是个整数。

Input

第一行一个正整数\(n\),表示序列长度。

第二行\(n\)个数,表示原序列\(a[1..n]\),第\(i\)个数表示\(a[i]\)

\(1≤n≤20,Sigma(|A_i|)<=10^9\),其中\(1<=i<=N.\)

Output

输出一个非负整数,表示答案。

Sample Input

2
-1 2

Sample Output

3

Solution

  • 注意到\(n\)很小,每个子集的权值和我们可以暴力计算得出.
  • 直接考虑各个子集作为最大前缀和.
  • 显然,一个子集\(S\)排列后能成为最大前缀和,那么这个排列中不能有负的后缀和(否则去掉会更优),剩下的数排列后不能有正的前缀和(否则加上会更优).
  • 我们令\(f[S]\)表示将\(S\)集合中的数排成没有负的后缀和的排列的方案数,\(g[S]\)表示将\(S\)集合中的数排成没有正的前缀和的排列的方案.
  • 那么易知答案即为\(\sum_{S\in U,sum[S]>=0}f[S]*g[\complement_{U}S]*sum[S]\).
  • 考虑如何快速计算出\(f\)\(g\).若对于一个集合\(i\),新增了一个数\(j\).(\(j\notin i\)).
  • 我们可以将\(i\)任意排列,再将\(j\)放在最后,方案数为\(f[i]\)\(g[i]\),统计入贡献.每个集合中的每个数都会被放在最后转移过来,所以总贡献一定是正确的.
  • 这样,只需要在加数的时候判断一下\(sum[i]\)的符号,即可确定转移\(f\)\(g\).
#include<bits/stdc++.h>
using namespace std;
typedef long long LoveLive;
inline int read()
{
	int out=0,fh=1;
	char jp=getchar();
	while ((jp>'9'||jp<'0')&&jp!='-')
		jp=getchar();
	if (jp=='-')
		{
			fh=-1;
			jp=getchar();
		}
	while (jp>='0'&&jp<='9')
		{
			out=out*10+jp-'0';
			jp=getchar();
		}
	return out*fh;
}
const int P=998244353;
const int MAXS=(1<<20)+10;
inline int add(int a,int b)
{
	return (a + b) % P;
} 
inline int mul(int a,int b)
{
	return 1LL * a * b % P;
}
int a[21];
int sum[MAXS],f[MAXS],g[MAXS];
int n;
inline int calc(int S)
{
	int res=0;
	for(int i=0;i<n && S;++i,S>>=1)
		if(S&1)
			res+=a[i];
	return res;
}
int main()
{
	n=read();
	for(int i=0;i<n;++i)
		a[i]=read();
	int S=1<<n;
	for(int i=0;i<S;++i)
		sum[i]=calc(i);
	for(int i=0;i<n;++i)
		f[1<<i]=1,g[1<<i]=1;
	for(int i=0;i<S;++i)
		{
			if(sum[i]>0)
				{
					for(int j=0;j<n;++j)
						if(!((i>>j)&1))
							f[i^(1<<j)]=add(f[i^(1<<j)],f[i]);
				}
			else
				{
					for(int j=0;j<n;++j)
						if(!((i>>j)&1))
							g[i^(1<<j)]=add(g[i^(1<<j)],g[i]);
				}
		}
	int ans=0;
	int U=S-1;
	g[0]=1;
	for(int i=0;i<S;++i)
		if(sum[U^i]<=0)
			ans=add(ans,mul(mul(f[i],sum[i]),g[U^i]));
	printf("%d\n",add(ans,P));
	return 0;
}

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-06-26
  • 2021-10-09
  • 2021-05-20
  • 2021-08-31
  • 2021-12-16
  • 2021-10-17
猜你喜欢
  • 2021-08-05
  • 2021-09-24
  • 2022-12-23
  • 2022-12-23
  • 2021-12-04
  • 2021-08-13
  • 2021-10-30
相关资源
相似解决方案