W
e
l
c
o
m
e
: )

关于一类树上路径操作的通用做法(毛毛虫剖分)

问法:

每次给出一条路径 \(S=(u, v)\),对所有 \(x\in S\)\(x\) 进行某种操作,对所有 \(x \in S, y\notin S, (x, y) \in Tree\) 进行另外一种操作。

简单来说就是路径操作涉及到所有路径上点的子节点。

xgf 鸽鸽 提醒,这玩意有个正式名字:毛毛虫剖分

通解

考虑我们重链剖分拍出的 dfn 序,它满足两个条件:

  • 每条重链上的所有点编号是连续的

  • 子树内的所有点编号是连续段

发现 [子树] 这个信息在操作并不重要,我们尝试将其改为:

  • 每条重链上的所有点编号是连续的

  • 每条重链上的所有的儿子(除去原本重链上的点)的编号也连续

这看起来是不可能的,考虑退一步,在满足第二条件时,尽量满足条件一。

根据参考资料不难得出一个简单的构造方法:

初始:把树根的重链塞入队列。

循环直到队空

  1. 取出队头的重链 \(Z\)

  2. 依次给 \(Z\) 上的点编号

  3. 依次给 \(Z\) 上的每个点的儿子编号(已经有的就不给),这些点必然是一条重链的 top(显然不包括 \(Z\) 中的点),将他们加进队列。

美中不足的是,重链 \(Z\)\(top\)\(Z\) 中的其他点可能分离,这个可以分讨解决。

要不,我们再添点限制?

  • 每条重链上的所有点编号是连续的

  • 每条重链上的所有的儿子(除去原本重链上的点)的编号也连续

  • 子树内的所有点编号是连续段

还是考虑尽量满足,这次标号换成递归式:

设现在递归到 x,先给 x 标号
若 x 是所在重链链顶,将 \(Z_{x}\) 的所有轻儿子标号
递归重儿子,然后递归轻儿子

void cov(int x){
	cat[x][0]=p+1;for(auto v:G[x]) if(dep[v]>dep[x]&&(v^son[x])) dfn[v]=++p;
	cat[x][1]=p;if(son[x]) cov(son[x]);
}
void dfs(int x, int rt){
	edp[top[x]=rt]=x;if(!dfn[x]) dfn[x]=++p;if(x==top[x]) cov(x);sub[x][0]=p+1;
	if(son[x]) dfs(son[x], rt);for(auto v:G[x]) if(!top[v]) dfs(v, v);sub[x][1]=p;
}

这样子能做到

  1. 一条重链为 \([dfn_{rt}, dfn_{rt}]\) and \([dfn_{son_{rt}}, dfn_{edp_{rt}}]\) 且 rt 的 dfn 序最小
  2. \([sub_{x, 0}, sub_{x, 1}]]\) 为子树中恰好除去了根重链所在毛毛虫脚的部分
  3. 一条重链部分 \((x, y)\) 的毛毛虫脚部分为 \([cat_{x, 0}, cat_{y, 1}]\)

例题:

NOI2021 轻重边,直接去看参考资料(

2022.09.14 SS 集训 T3 洛可可天使

每个点维护到父亲的边的边权的期望值,每次操作其实就是 \(1-x\) 路径上的点权 \(\times (1-p_i)+p_i\times c_i\)(有 \((1-p_i)\) 的概率不变,剩下有 \(p_i\) 的概率变为 \(c_i\)),以及这条链所有的旁系儿子点权 \(\times (1-p_i)\)

考虑按照上面的做法拍成 dfs 序,直接暴力线段树即可。

注意向上跳的过程中,即将跳的上面的链的底部重儿子是不会被算入旁系儿子的,要单独乘一次,我当前链的顶部可能会在上面被当成旁系儿子,所以记得乘加的时候跳过这个点(裂开成两个区间)。

复杂度 \(O(m\log^2n)\)

P8479 「GLR-R3」谷雨

参考资料:

https://www.luogu.com.cn/blog/cyh-toby/solution-p7735

https://www.cnblogs.com/A-Quark/p/16435243.html

你发现了,其实是复读一遍参考资料

posted @ 2022-09-14 16:18  127_127_127  阅读(374)  评论(1编辑  收藏  举报