树上差分常用在对路径权值的快速修改操作。
我们可以先借助线性差分来理解。
线性差分常用在线性修改区间的值,我们通过修改区间边界的值,起点加上值表示给后面的区间都加上权值,终点减去值表示为后面的区间都减去权值,为后面不应该受影响的区间抵消掉该影响。最后再做一个前缀和,则就能将差分值转化为每个点在修改后的权值了。
那么将线性差分转化为树上差分。
我们将线性差分中的"区间修改"概念理解为"树的两结点的路径上所有边的权值修改"。
则"起点加上值"概念可以理解为"一个结点到根节点的路径上所有边都加上权值",
"终点减去值"概念可以理解为"该结点与另一个结点的【最近公共祖先】到【根节点】的路径上的所有边都减去权值以抵消掉影响"。(求最近公共祖先可以看我的另一篇文章:最近公共祖先结点--LCA(倍增法,言简意赅)-优快云博客
那么两结点的"区间修改"也就是“两结点的差分值各自+1,最近公共祖先的差分值每次-1(总共减2)"
“前缀和”可以理解为“结点与其所有子树的权值之和”。
则前缀和就是该结点的入边的权值。
vector<int>son[N];
int br[N];//每个结点的差分值
int w[N];//点的入边的权值
//"区间修改"
void Path(int a,int b){
br[a]++;br[b]++;
br[lca(a,b)]-=2;
}
//计算"前缀和",即点的入边的权值
int getSum(int u,int p){
for(auto it:son[u]){
if(it!=p){
w[u]+=getSum(it,u);
}
}
return w[u];
}