• [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;
    }
    
    

  • 相关阅读:
    20.12.21 leetcode316
    20.12.18 leetcode389
    NOIP2017退役记
    DNA序列 LOJ NOIP模拟赛 D1T1 字符串哈希
    解药还是毒药 codevs2594 状态压缩 BFS
    换教室 vijos2005 NOIP2016 D1T3 期望DP 图论最短路(雾)
    都市大飙车 UESTC 1652 概率DP
    添加括号 vijos1038 动态规划 区间DP
    一道神奇的并查集
    还有一道神奇的暴力(正解是要旋转坐标轴的)
  • 原文地址:https://www.cnblogs.com/rvalue/p/10934984.html
Copyright © 2020-2023  润新知