zoukankan      html  css  js  c++  java
  • [LOJ 6433][PKUSC 2018]最大前缀和

    [LOJ 6433][PKUSC 2018]最大前缀和

    题意

    给定一个长度为 (n) 的序列, 求把这个序列随机打乱后的最大前缀和的期望乘以 (n!) 后对 (998244353) 取膜后的值.

    前缀和不能为空.

    (nle 20).

    题解

    首先这个期望显然是逗你玩的...只是计数而已

    然后我们把一个序列拆成两部分, 一部分前缀和都不大于总和, 一部分前缀和都不大于 (0). 那么显然这样的一个序列的最大前缀和就是第一部分的和. 我们只要知道有多少个这样的序列就好了.

    后面的做法感觉有点意思

    我们用两个DP分别求解序列的某个子集组成两个部分的方案数量. 如果当前集合的和大于 (0) 那么显然不能用来组成第二部分, 但是我们可以在这个集合产生的合法第一部分的前面加一个值来组成新的第一部分. 而如果当前集合的和不大于 (0), 那么它只能用于构成第二部分, 而且我们可以断定如果在这个集合中钦定某个值放在最后, 那么只要剩下的值能构成合法的第二部分, 新的序列也能构成合法的第二部分.

    最后枚举那些值在第一部分, 剩下值丢给第二部分, 卷起来就可以了.

    参考代码

    #include <bits/stdc++.h>
    
    const int MAXN=21;
    const int MOD=998244353;
    const int MAXL=(1<<20)|3;
    
    int n;
    int a[MAXN];
    int dp1[MAXL];
    int dp2[MAXL];
    int sum[MAXL];
    
    inline int LowBit(int);
    
    int main(){
    	scanf("%d",&n);
    	dp2[0]=1;
    	for(int i=0;i<n;i++){
    		scanf("%d",a+i);
    		dp1[1<<i]=1;
    		sum[1<<i]=a[i];
    	}
    	for(int s=1;s<(1<<n);s++){
    		if(s!=LowBit(s))
    			sum[s]=sum[s^LowBit(s)]+sum[LowBit(s)];
    		if(sum[s]>0){
    			for(int i=0;i<n;i++)
    				if((s&(1<<i))==0)
    					(dp1[s^(1<<i)]+=dp1[s])%=MOD;
    		}
    		else{
    			for(int i=0;i<n;i++)
    				if((s&(1<<i))!=0)
    					(dp2[s]+=dp2[s^(1<<i)])%=MOD;
    		}
    	}
    	int ans=0;
    	for(int s=1;s<(1<<n);s++)
    		(ans+=1ll*sum[s]*dp1[s]%MOD*dp2[((1<<n)-1)^s]%MOD)%=MOD;
    	printf("%d
    ",ans<0?ans+MOD:ans);
    	return 0;
    }
    
    inline int LowBit(int x){
    	return x&-x;
    }
    
    

  • 相关阅读:
    面向过程, 面向对象, 类和对象, 类与数据类型
    python函数
    简单的登录注册函数
    Java遍历包中所有类
    spring boot jar启动
    过期算法
    负载均衡算法
    spring boot druid mybatis多数据源
    多线程wait和notify实现1212
    多线程售票
  • 原文地址:https://www.cnblogs.com/rvalue/p/10934984.html
Copyright © 2011-2022 走看看