bzoj 5369 最大字首和
阿新 • • 發佈:2018-11-27
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;
}