目录

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

迁移提示
本文迁移自博客园, 原文链接

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

题意

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

前缀和不能为空.

$n\le 20$.

题解

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

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

后面的做法感觉有点意思

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

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

参考代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#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\n",ans<0?ans+MOD:ans);
	return 0;
}

inline int LowBit(int x){
	return x&-x;
}

https://pic.rvalue.moe/2021/08/02/3a802cd63a879.png