题目
给一棵有N个节点的树,边权非负。有M个操作。
操作有2种:
- 指定子树中的边权都加上某个数。
- 输出节点x到节点y的路径上的边权的平方和。
数据范围
$ 0 \leq N, M \leq 200000 $
做法
下面的2种做法,一般都是处理点权的。对于边的边权,要将其转化为终点t的点权。
下面讲一下如何用线段树维护区间平方和。
对于区间[l, r),其中朝向叶子的边的个数为np,朝向根的边的个数nn=r-l-np。朝向叶子的边的权值和为sp,朝向根的边的权值和为sn,整个区间的边权平方和为s2。
那么,对区间的边权都加上c,只要做如下处理:
|
|
DFS序+线段树
在求DFS序的时候,每条边都会经过2次。经过某条边时,如果方向是朝叶子方向,那么我们就令边权为原来的边权,而如果方向是朝根的方向,我们就令边权为原来边权的相反数。
用线段树来维护DFS序上的边权。因为同一子树内的点的DFS序是连续的一段,那么操作1就是线段树的区间修改。对于操作2,由于我们给赋值边权的方法,线段树上id[lca(x, y)]到id[x]的这段区间的和加上id[lca(x, y)]到id[y]的这段区间的和就是答案,因为重复经过的边求和时正负相消。(id[x]:进入点x时DFS的时间戳)。
复杂度是$O(M \times log_2N)$。
树链剖分+线段树
由于树链剖分有这个性质:同一子树内的点在线段树上的标号也是连续的一段,所以这题也能用树链剖分来做。操作1就是区间修改,操作2就是区间求和。
复杂度是$O(M \times log_2N \times log_2N)$
代码
DFS序+线段树
|
|
树链剖分+线段树
|
注意
- 最好新开一个数组标记一下这条边的朝向,而不是靠边权的正负来判断,这样适应性更强。
- 我的线段树递归的时候是不判断区间的合法性的,要在调用的时候就判断合法性。