链接:https://ac.nowcoder.com/acm/contest/180/E
来源:牛客网
树上路径
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
给出一个n个点的树,1号节点为根节点,每个点有一个权值
你需要支持以下操作
1.将以u为根的子树内节点(包括u)的权值加val
2.将(u, v)路径上的节点权值加val
3.询问(u, v)路径上节点的权值两两相乘的和
输入描述:
第一行两个整数n, m,表示树的节点个数以及操作个数
接下来一行n个数,表示每个节点的权值
接下来n - 1行,每行两个整数(u, v),表示(u, v)之间有边
接下来m行
开始有一个数opt,表示操作类型
若opt = 1,接下来两个整数表示u, val
若opt = 2,接下来三个整数表示(u, v), val
若opt = 3,接下来两个整数表示(u, v)
含义均如题所示
输出描述:
对于每个第三种操作,输出一个数表示答案,对10^9+710
9
+7取模
示例1
输入
复制
3 8
5 3 1
1 2
1 3
3 1 2
3 1 3
3 2 3
1 1 2
2 1 3 2
3 1 2
3 1 3
3 2 3
输出
复制
15
5
23
45
45
115
说明
第一组询问结果:3 * 5 = 15
第二组询问结果:1 * 5 = 5
第三组询问结果:3 * 5 + 1 * 5 + 3 * 1 = 23
备注:
对于30 %30%的数据,n, m \leqslant 100n,m⩽100
对于100 %100%的数据,n, m \leqslant 10^5n,m⩽10
5
设a_ia
i
表示读入的第i个节点的权值以及每次修改的权值,保证a_i \leqslant 10^4a
i
⩽10
4
保证不会有负数
思路:
很显然的树链剖分题,
前两个操作都是树链剖分的常规操作,
我们来看下第三个操作,我们假设询问的路径上有3个节点,权值分别是 a,b,c,,我们所求的结果就是ab+ac+b*c 来看下如何得到这个结果呢?
即我们可以维护区间的两个值(节点权值的sum和,和节点权值平方的sum)来得出区间的两两相乘再相加的结果(即操作3的输出)。
我们知道线段树维护区间sum和是很容易的,这里我就不讲了,那么如何维护权值平方的sum呢(即当区间中每一个值都加上t,如何方便得到新的平方sum和)?
我们假设一次更改的区间有三个节点,权值分别是a,b,c
这样我们可以看出,我们可以从线段树维护的两个值 :sum和平方sum 来更新平方sum
例如区间加上t
新的平方sum= 更新前的平方sum+ 区间长度 乘 t 乘 t + 2t 乘 权值sum和。
这样就可以写本题了,记得pushdown的时候线段树的laze标记一定是+= 而不是 =
细节见代码:
#include
#include
#include
#include
#include
#include
#include
#include