树上差分
问题引入
当我们要对某条路径进行
+
+
+ 或
−
-
− 操作时,我们应该怎么做?
比如说
N
o
d
e
1
Node1
Node1 到
N
o
d
e
2
Node2
Node2 的路径,先分为
N
o
d
e
1
Node1
Node1 到 LCA 和 LCA 到
N
o
d
e
2
Node2
Node2 两条链。
如图:
然后,我们开一个
T
a
g
Tag
Tag 数组打标记。
我们让
T
a
g
[
N
o
d
e
1
]
+
+
Tag[Node1]++
Tag[Node1]++,
T
a
g
[
N
o
d
e
2
]
+
+
Tag[Node2]++
Tag[Node2]++,
T
a
g
[
L
C
A
]
−
−
Tag[LCA]--
Tag[LCA]−−,
T
a
g
[
F
a
[
L
C
A
]
]
−
−
Tag[Fa[LCA]]--
Tag[Fa[LCA]]−−。
此时,我们会发现,每个点的权值变化相当于它的子树的标记和!
这就是树上差分。
注意:这仅适用于多次修改后在进行查询,如果中途有多次查询是无法解决的。
LCA
方法
使用倍增。
记
F
a
[
N
o
d
e
]
[
i
]
Fa[Node][i]
Fa[Node][i] 表示点
N
o
d
e
Node
Node 的
2
i
2^i
2i 级祖先,
F
a
[
N
o
d
e
]
[
0
]
Fa[Node][0]
Fa[Node][0] 就是它的父节点。
那么对于
i
⩾
1
i \geqslant 1
i⩾1,我们有转移式:
F
a
[
N
o
d
e
]
[
i
]
=
F
a
[
F
a
[
N
o
d
e
]
[
i
−
1
]
]
[
i
−
1
]
Fa[Node][i]=Fa[Fa[Node][i-1]][i-1]
Fa[Node][i]=Fa[Fa[Node][i−1]][i−1]。
也就是说,点
N
o
d
e
Node
Node 的
2
i
2^i
2i 级祖先就是点
N
o
d
e
Node
Node 的
2
i
−
1
2^{i-1}
2i−1 级祖先 的
2
i
−
1
2^{i-1}
2i−1 级祖先。(
2
i
−
1
+
2
i
−
1
=
2
i
−
1
∗
2
=
2
i
2^{i-1}+2^{i-1}=2^{i-1}*2=2^i
2i−1+2i−1=2i−1∗2=2i)
对于树上的每个点我们都能处理
F
a
Fa
Fa 的值。
求
N
o
d
e
1
Node1
Node1 和
N
o
d
e
2
Node2
Node2 的 LCA 时,假设深度
D
e
p
[
N
o
d
e
1
]
>
D
e
p
[
N
o
d
e
2
]
Dep[Node1] > Dep[Node2]
Dep[Node1]>Dep[Node2],我们先计算出深度差
D
i
f
=
D
e
p
[
N
o
d
e
1
]
−
D
e
p
[
N
o
d
e
2
]
Dif = Dep[Node1] - Dep[Node2]
Dif=Dep[Node1]−Dep[Node2],然后把
D
e
p
Dep
Dep 转成二进制,通过倍增跳跃使得点
N
o
d
e
1
Node1
Node1 和点
N
o
d
e
2
Node2
Node2 深度相同(一定要从高位往低位跳)。
然后,若点
N
o
d
e
1
Node1
Node1 和点
N
o
d
e
2
Node2
Node2 相同,则
N
o
d
e
1
Node1
Node1 (或
N
o
d
e
2
Node2
Node2) 就是 LCA。
否则,我们先考虑
2
20
2^{20}
220 (
2
2
2 的某个次幂,只要满足
>
N
> N
>N 就行),如果
N
o
d
e
1
Node1
Node1 和
N
o
d
e
2
Node2
Node2 同时跳
2
20
2^{20}
220 级祖先仍然不同,那么就跳,否则不动;接着枚举
2
19
2^{19}
219,以此类推。(注意不能越界)
到最后,
N
o
d
e
1
Node1
Node1,
N
o
d
e
2
Node2
Node2 只需要再跳一步就是 LCA 了。时间复杂度是
Θ
(
log
N
)
\Theta(\log N)
Θ(logN) 的。
倍增原理是不难的,但是应用是非常广泛的,大家需要练就的是用倍增解决具体问题的能力!!
代码(LCA)
void DFS(int Current, int Previous) {
// std::cout << "DFS\n";
Tree[Current][0] = Previous;
for (int i = 1; i <= Log2[Dep[Current]]; i++) {
Tree[Current][i] = Tree[Tree[Current][i - 1]][i - 1];
}
for (int Node2 : Gr[Current]) {
Dep[Node2] = Dep[Current] + 1;
DFS(Node2, Current);
}
}
int LCA(int A, int B) {
//std::cout << "LCA\n";
if (Dep[A] < Dep[B]) std::swap(A, B);
while (Dep[A] > Dep[B]) A = Tree[A][Log2[Dep[A] - Dep[B]]];
if (A == B) return A;
for (int i = Log2[Dep[A]]; i >= 0; i--) {
if(Tree[A][i] != Tree[B][i]) {
A = Tree[A][i];
B = Tree[B][i];
}
}
return Tree[A][0];
}