1.树上差分概念

树上差分,字面意思就是在树上做差分。

所以她能干啥能,举个例子,如果题目问经过树上某个点或某个边的次数,树上差分就可以派上用场啦。

树上差分就是利用差分的性质,前缀和的思想。只对树上一部分节点进行修改。而不是暴力全改,能将O(n)O(n)的修改降为O(1)O(1)

这个在日后会经常用到,要好好学习。

2.点差分

咱们一个一个加肯定包会TLE的,但是我们在讲树上差分啊!

那么我们如何进行差分呢,差分是在一条链上的在树上怎么操作呢?唉!就是将树拆成链

如下

pEANli6.png

对路径363\rightarrow 6路径进行加一的操作我们先找到3和6的LCA即2,将这个路径转化为2条链,一个是232\rightarrow 3565\rightarrow 6,如下

pEAN3RO.png

让后分别进行差分,红色和橙色对应上面的链

pEAN8zD.md.png

等会,为什么非要是565\rightarrow 6而不是262\rightarrow 6呢?因为232\rightarrow 3的路径已经将2已经加过了啊。

为什么我们用LCA呢,我们假设2种情况,第一种情况就是要加的树都在一个链上,这时候LCA就是深度最小节点的的父亲,例如对232\rightarrow 3进行操作。第二种就是像上述363\rightarrow 6拐过来,我们用LCA就可以求出这个路径的拐点,拐点就是LCA(3,6)=2LCA(3,6)=2。让后像上面一样拆成2条链

让后跑一遍Dfs就可以统计出来答案啦。

例题P3128 [USACO15DEC] Max Flow P

其实就是上面的点差分经典例题

这里LCA我们使用的是倍增求LCA

小技巧:

1
__lg(x)

这个函数可以直接搞出来整数的log2xlog_2x

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
int n,m;
const int MN=5e4+15,ML=40;
int fa[MN][ML],dep[MN],ml,sum[MN],ans=-1;
vector<int> adj[MN];
void dfs(int u,int pre){
fa[u][0]=pre;
dep[u]=dep[pre]+1;
for(int i=1;i<=ml;i++){
fa[u][i]=fa[fa[u][i-1]][i-1];
}
for(auto v:adj[u]){
if(v!=pre){
dfs(v,u);
}
}
}
int lca(int x,int y){
if(dep[x]>dep[y]){
swap(x,y);
}
for(int i=ML-1;i>=0;i--){
if(dep[fa[y][i]]>=dep[x]){
y=fa[y][i];
}
}
if(x==y) return x;
for(int i=__lg(dep[x]);i>=0;i--){
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
void dfss(int u,int fa){
for(auto v:adj[u]){
if(v!=fa){
dfss(v,u);
sum[u]+=sum[v];
}
}
ans=max(ans,sum[u]);
}
int main(){
cin>>n>>m;
ml=__lg(n);
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
adj[u].push_back(v);
adj[v].push_back(u);
}
dfs(1,0);
while (m--)
{
int s,t;
cin>>s>>t;
int l=lca(s,t);
sum[s]++;
sum[t]++;
sum[l]--;
if(l!=1){
sum[fa[l][0]]--;
}
}
dfss(1,0);
cout<<ans;
return 0;
}

双倍经验 P3258 [JLOI2014] 松鼠的新家

3.边差分

其实差不多

还是上面那个图

这回紫色使我们要访问的边,并加上1的边权。但是我们不能给边加边权不然不太好差分,我们只能把边权硬塞给点。

发现这不刚好可以拆成两条“链”吗,一个就是单独的33,一个565\rightarrow 6,那我们只需要sum[3]sum[3]++,sum[6]sum[6]–,sum[LCA(3,6)]sum[LCA(3,6)]-=2就ok啦

同样也只需要dfs1遍就可以统计出来答案啦(十分甚至九分的厉害

至于为啥操作不同请自行用拆链的思想思考

4.BB

树上差分之后用处很多的,尤其是重复的区间操作或贡献问题,有的时候其实也可用树链剖分取做(但是我不会),如果对您有帮助,请不要忘了点赞!(≧∇≦)ノ