[PKUWC2018]Minimax

题意:

n(3×105) 个结点的二叉树,叶子结点有互不相同的权值 v, 非叶子节点有概率 p 取两个子节点最大值,否则取最小值。

i=1mividi2, 即 m 种可能的结果中,第 i 小的权值 vi * i * 根节点是 vi 的概率的平方。

树形dp

由于权值互不相同,所有的可能个数就是叶子节点的个数,只要求出根结点是出现某个权值的概率, 问题就可以解决了。

因为需要比较大小,得确定当前节点的权值。

方程

可以设出方程 fu,x, 表示当前节点 u 的权值是 第 x 大的权值 val 的概率。

初始状态

对于每个叶子节点 u,这个点是 vu 的概率肯定是 1

因此: fu,x=1

转移

分类讨论当前非叶子节点 u 的权值 val 从哪里来,令 val 是第 x 大的权值。

  1. 如果只有一个儿子,那就一定是从这个儿子来:

  2. 考虑从左节点来,有最大值和最小值两种途径。

    1. 最大值:

      fu,x=fl,x×i=1x1fr,i×p

    2. 最小值:

      fu,x=fl,x×i=x+1mfr,i×(1p)

对于右节点同理。

就有:

fu,x=fl,xi=1x1fr,i×p+fl,xi=x+1mfr,i×(1p)+fr,xi=1x1fl,i×p+fr,xi=x+1mfl,i×(1p)

化简:

fu,x=fl,x(i=1x1fr,i×p+i=x+1mfr,i×(1p))+fr,x(i=1x1fl,i×p+i=x+1mfl,i×(1p))

分析

时间复杂度 O(n×m), 空间复杂度 O(n×m)

对于空间,只有根节点才有用,没必要开那么大,可以用到类似滚动数组的玩意,但要在树上?

对于时间,用前后缀和优化的转移是 O(m)的,还是不够好?

线段树合并

对于上述问题

线段树合并的空间是 O(mlogm) 的,因为加入新点的空间花费是 O(logm), 最多插入 O(m) 次。

转移的形式像对应点相加,在乘一个值 P, 简单表示为 fi=f1i×P1+f2i×P2

而这个 P1,P2 可以在合并的时候顺路求出, 只用维护区间和就能做到。

如果遇到某个儿子的结点为空,其中的 fl,xfr,x, 那么打一个乘法的懒标记即可。

线段树合并时间复杂度均摊是 O(nlogm) 的。

代码

#include<bits/stdc++.h>

using namespace std;

using ll = long long;
const int MAXN = 300010;
const int INF = 0x7fffffff;
const int mod = 998244353;

template <typename T>
void Read(T &x) {
	x = 0; T f = 1; char a = getchar();
	for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
	for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
	x *= f;
}

int add(int a, int b) {
	int c = a + b;
	if (c >= mod) c -= mod;
	if (c < 0) c += mod;
	return c; 
} 
int mul(int a, int b) {
	return 1ll * a * b % mod; 
}
int qpow(int a, int b) {
	int sum(1);
	while(b) {
		if (b & 1) sum = mul(sum, a);
		a = mul(a, a);
		b >>= 1;
	}
	return sum; 
}

int n; 
vector<int> e[MAXN];
int val[MAXN]; 

int cnt;
int L[MAXN << 5], R[MAXN << 5], Mul[MAXN << 5], sum[MAXN << 5]; 

int newnode() {
	int rt = ++ cnt;
	L[rt] = R[rt] = sum[rt] = 0;
	Mul[rt] = 1;
	return rt; 
}

void pushup(int rt) {
	sum[rt] = add(sum[L[rt]], sum[R[rt]]);  
}
void _mul(int rt, int val) {
	if (!rt) return ; 
	sum[rt] = mul(sum[rt], val); 
	Mul[rt] = mul(Mul[rt], val); 
}
void pushdown(int rt) {
	if (!rt) return ;
	if (L[rt]) _mul(L[rt], Mul[rt]);
	if (R[rt]) _mul(R[rt], Mul[rt]);
	Mul[rt] = 1; 
}
int update(int _L, int _C, int l, int r, int rt) {
	if (!rt) rt = newnode(); 
	if (l == r) {
		sum[rt] = _C;
		return rt; 
	}
	pushdown(rt); 
	int m = (l + r) >> 1; 
	if (_L <= m) L[rt] = update(_L, _C, l, m, L[rt]);
	else R[rt]= update(_L, _C, m + 1, r, R[rt]);
	pushup(rt); 
	return rt; 
}
int merge(int x, int y, int sum1, int sum2, int p, int l, int r) {
	if (!x && !y) return 0; 
	if (!x) {
		_mul(y, sum2);
		return y;
	} 
	if (!y) {
		_mul(x, sum1);
		return x; 
	}
	pushdown(x), pushdown(y); 
	int m = (l + r) >> 1; 
	int ls1 = sum[L[x]], ls2 = sum[L[y]], rs1 = sum[R[x]], rs2 = sum[R[y]]; 
	L[x] = merge(L[x], L[y], add(sum1, mul(add(1, -p), rs2)), add(sum2, mul(add(1, -p), rs1)), p, l, m);
	R[x] = merge(R[x], R[y], add(sum1, mul(p, ls2)), add(sum2, mul(p, ls1)), p, m + 1, r);
	pushup(x); 
	return x;   
}

int len;
int b[MAXN];

int root[MAXN]; 
void dfs(int u) {
	if (!e[u].size()) {
		root[u] = update(lower_bound(b + 1, b + len + 1, val[u]) - b, 1, 1, len, root[u]); 
	} else if(e[u].size() == 1) {
		int son = e[u][0];
		dfs(son);
		root[u] = root[son];	
	} else {
		int l = e[u][0], r = e[u][1]; 
		dfs(l), dfs(r); 
		root[u] = merge(root[l], root[r], 0, 0, val[u], 1, len);
	}
}

int query(int l, int r, int rt) {
	if (!rt) return 0;
	if (l == r) {
		return mul(mul(l, b[l]), qpow(sum[rt], 2)); 
	}
	int m = (l + r) >> 1;
	pushdown(rt); 
	return add(query(l, m, L[rt]), query(m + 1, r, R[rt])); 
}
 
int main() {
	Read(n);
	for (int i = 1; i <= n; i ++) {
		int fa;
		Read(fa);
		e[fa].emplace_back(i); 
	}
	
	for (int i = 1; i <= n; i ++) {
		Read(val[i]); 
		if (e[i].size())
			val[i] = mul(val[i], qpow(10000, mod - 2)); 
		else 
			b[++ len] = val[i]; 
	}
	sort(b + 1, b + len + 1);
	len = unique(b + 1, b + len + 1) - b - 1;
	dfs(1); 
	cout << query(1, len, root[1]); 
	return 0;
} 
posted @   qjbqjb  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示