跳到主要内容

树上差分

本人被树上差分困扰了很久!!今天正式来攻克一下。

快速回顾:差分和前缀和

考虑这样一个问题:给一个数列 {ai}1n\{a_i\}^n_1,初始时都是 00,要求支持以下两种操作:

  • 给定 [L,R],k[L,R],k:让 aLaRa_L\to a_R 增加 kk

  • 给定 xx:输出 axa_x

先不要想什么线段树,有什么可以不暴力修改每个节点的方法吗?当然有!我们可以维护一个差分数组。修改 [L,R][L,R] 时,

  • bLbL+kb_L\leftarrow b_L+k

  • bR+1bR+1kb_{R+1}\leftarrow b_{R+1}-k

要查询 axa_x 时,我们就计算 b1bxb_1\to b_x 的前缀和就好了。

什么是树上差分

树上差分解决这样的问题:

  • 给定树上两个节点 u,vu,v 和数 kk:让 uvu\to v 这条路径上的计数器 +k+k

和数列上的差分有些不同,这里应用的是 “子树和” 思想。同时又分为点差分边差分两种。

点差分

图摘自 oi-wiki

如图,我们想要让路径 STS\to T 上的节点计数器 +k+k,这样做:

  • cntScntS+k\text{cnt}_S\leftarrow\text{cnt}_S+kcntTcntT+k\text{cnt}_T\leftarrow\text{cnt}_T+k。(把端点的差分计数器 +k+k

  • cntLCAcntLCAk\text{cnt}_{LCA}\leftarrow\text{cnt}_{LCA}-k。(把 LCA 的差分计数器 k-k

  • cntf(LCA)cntf(LCA)k\text{cnt}_{f(LCA)}\leftarrow\text{cnt}_{f(LCA)}-k。(把 LCA 的父节点的差分计数器 k-k

你可能会疑惑,那我们怎么获取,比如 cntLCA\text{cnt}_{LCA} 的值呢?我们只要计算以 LCALCA 为顶点的子树的差分计数器之和就行了!!神奇!