zoukankan      html  css  js  c++  java
  • [PKUSC2018]最大前缀和

    [PKUSC2018]最大前缀和

    题目大意:

    (n(nle20))个数(A_i(|A_i|le10^9))。求这(n)个数在随机打乱后最大前缀和的期望值与(n!)的积在模(998244353)意义下的值。其中最大前缀和的定义为(forall iin[1,n]sum_{j=1}^iA_j)的最大值。

    思路:

    考虑一个分界点(p),使得(sum A_{1sim p})为最大前缀和,那么显然(p)之后的所有前缀和均(<0),否则就存在可以替换(p)的方案使得前缀和更大。

    (sum[i])表示子集(i)的数值和,(f[i])表示最大前缀和为(sum[i])的方案数,(g[i])表示任意前缀和均为负的方案数。(f[i])(g[i])均可以通过动态规划求得。最后答案即为(sum sum[S] imes f[S] imes g[overline S])。时间复杂度(mathcal O(2^nn))

    源代码:

    #include<cstdio>
    #include<cctype>
    typedef long long int64;
    inline int getint() {
    	register char ch;
    	register bool neg=false;
    	while(!isdigit(ch=getchar())) neg|=ch=='-';
    	register int x=ch^'0';
    	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    	return neg?-x:x;
    }
    const int N=20,mod=998244353;
    int a[N],f[1<<N],g[1<<N];
    int64 sum[1<<N];
    int main() {
    	const int n=getint(),u=(1<<n)-1;
    	g[0]=1;
    	for(register int i=0;i<n;i++) {
    		f[1<<i]=1;
    		a[i]=getint();
    		for(register int s=0;s<1<<n;s++) {
    			if(s&(1<<i)) sum[s]+=a[i];
    		}
    	}
    	for(register int s=1;s<1<<n;s++) {
    		if(sum[s]>0) {
    			for(register int i=0;i<n;i++) {
    				if(!(s&(1<<i))) {
    					(f[s|(1<<i)]+=f[s])%=mod;
    				}
    			}
    		} else {
    			for(register int i=0;i<n;i++) {
    				if(s&(1<<i)) {
    					(g[s]+=g[s^(1<<i)])%=mod;
    				}
    			}
    		}
    	}
    	int ans=0;
    	for(register int i=1;i<1<<n;i++) {
    		(ans+=(int64)sum[i]*f[i]%mod*g[u^i]%mod)%=mod;
    	}
    	printf("%d
    ",(ans+mod)%mod);
    	return 0;
    }
    
  • 相关阅读:
    C++ 友元(friend关键字)、类中的重载、操作符重载(operator关键字)
    C++ 二阶构造模式
    C++ 对象构造顺序、构析函数、临时对象。
    C++ 初始化列表
    C++ 对象的构造
    C++ 类学习笔记 :: 作用域限定符
    linux和window下生成任意大小的文件
    RobotFramework和Eclipse集成-安装和使用说明
    Linux中判断一个命令是否执行成功
    xpath 轴定位表达方式
  • 原文地址:https://www.cnblogs.com/skylee03/p/9154475.html
Copyright © 2011-2022 走看看