树上差分
树上差分顾名思义也就是在树上做差分,一般用于区间修改操作,通过只改变区间头和区间尾来实现对整个区间值的修改。树上差分又分为点差分和边差分,一般来说要用到lca。
点差分
基本模型:有 n n n次修改操作,每次把 u − > v u->v u−>v的所有点权都加 x x x,最后问点权最大的为多少。
算法分析:可以先从线性的差分着手,线性差分中是 r a t e [ u ] + = x , r a t e [ v + 1 ] − = x rate[u]+=x,rate[v+1]-=x rate[u]+=x,rate[v+1]−=x,之后求出的前缀和就是修改过后的数值。再回到这个问题,我们可以将 r a t e [ u ] + = x , r a t e [ v ] + = x , r a t e [ l a c ( u , v ) ] − = 2 ∗ x rate[u]+=x,rate[v]+=x,rate[lac(u,v)]-=2*x rate[u]+=x,rate[v]+=x,rate[lac(u,v)]−=2∗x,求出子树和之后就是当前点的点权了。(注意有些题目可能会需要将边权下放为点权,下放的方法又分为两种:①直接将边权作为终点的点权。②在边的两点之间再加一个点,将这个点与两点连一条0权边,然后将原边权作为该点权值)。
这里找来一张图供大家参考
图片转自Sagittarius
下面边差分也要用到它
例题 —— 闇の連鎖
题目描述
传说中的暗之连锁被人们称为Dark。Dark 是人类内心的 黑暗的产物,古今中外的勇者们都试图打倒它。经过研究, 你发现Dark 呈现无向图的结构,图中有N 个节点和两类边, 一类边被称为主要边,而另一类被称为附加边。Dark 有N – 1 条主要边,并且Dark 的任意两个节点之间都存在一条只由主 要边构成的路径。另外,Dark 还有M 条附加边。
你的任务是把Dark 斩为不连通的两部分。一开始Dark 的附加边都处于无敌状态,你只能选择一条主要边切断。一 旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切 断。但是你的能力只能再切断Dark 的一条附加边。现在你想要知道,一共有多少种方案可 以击败Dark。注意,就算你第一步切断主要边之后就已经把Dark 斩为两截,你也需要切断 一条附加边才算击败了Dark。
输入格式
第一行包含两个整数N 和M。
之后N – 1 行,每行包括两个整数A 和B,表示A 和B 之间有一条主要边。
之后M 行以同样的格式给出附加边。
输出格式
输出一个整数表示答案。
样例输入
4 1
1 2
2 3
1 4
3 4
样例输出
3
数据范围与约定
对于20% 的数据, N ≤ 100 , M ≤ 100 N≤100,M≤100 N≤100,M≤100。
对于100% 的数据, N ≤ 100000 , M ≤ 200000 N≤100 000,M≤200 000 N≤100000,M≤200000。数据保证答案不超过 2 31 – 1 2^{31 }– 1 231–1。
题解
因为当 ( x , y ) (x,y) (x,y)路径上的任意一条主要边消失后,附加边都可以成为主要边,去维护连通性。
因此现在我们的问题模型转化了。给定一个 n − 1 n−1 n−1条边的树,求每一条树边,被非树边覆盖了多少次。(树边也就是主要边,非树边也就是附加边)。
这其实就是一个树上差分问题:每一条附加边,使得(x,y)节点的路径上,每一个节点的权值+1。
c o d e code code
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+