zoukankan      html  css  js  c++  java
  • bzoj 5369 最大前缀和

    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_{Sin U,sum[S]>=0}f[S]*g[complement_{U}S]*sum[S]).
    • 考虑如何快速计算出(f)(g).若对于一个集合(i),新增了一个数(j).((j otin 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
    ",add(ans,P));
    	return 0;
    }
    
  • 相关阅读:
    VC 常见问题百问
    python windows 环境变量
    Check server headers and verify HTTP Status Codes
    Where are the AES 256bit cipher suites? Please someone help
    outlook 如何预订会议和会议室
    安装Axis2的eclipse插件后,未出现界面
    windows 环境变量
    python 时间日期处理汇集
    openldap学习笔记(使用openldap2.3.32)
    set p4 environment in windows
  • 原文地址:https://www.cnblogs.com/jklover/p/10024388.html
Copyright © 2011-2022 走看看