斜二进制 LCA
本文介绍了一种基于斜二进制的 LCA 算法,算是一个省流,完整版请看 https://www.luogu.com.cn/article/u81tks5o。
写在前面:优劣一览。
本文介绍的算法此前在 Codeforces 有所出现,但是并未被引入 OI。笔者独立发明该算法(及其相关体系)后认为其值得被引入。其在 LCA 问题上有较大优势。
该算法复杂度为
太长不看版:
void dfs(int x) {
int p = fa[x], q = lb[p], r = lb[q];
d[x] = d[p] + 1;
lb[x] = d[p] - d[q] != d[q] - d[r] ? p : r;
for (int y : es[x])
if (y != p) fa[y] = x, dfs(y);
}
int lca(int u, int v) {
if (d[u] < d[v]) swap(u, v);
while (d[u] > d[v])
if (d[lb[u]] < d[v])
u = fa[u];
else
u = lb[u];
while (u != v)
if (lb[u] == lb[v])
u = fa[u], v = fa[v];
else
u = lb[u], v = lb[v];
return u;
}
如果不想了解原理的话可以直接背板,它并不长。
- 斜二进制
斜二进制是一种奇怪的进制。它从低到高第
我们将使用一种归纳构造的方式来生成每个自然数的斜二进制分解。0 的斜二进制是 0。我们假设已经对于
不妨这是第
在第一位上加一即可。
不难验证以此法生成的斜二进制分解符合它应有的性质。
这么讲可能比较抽象,我们来举点例子。
-
0 的斜二进制是 0。
-
1 的斜二进制是 1。因为 0 的最低有效位不为二。
-
2 的斜二进制是 2。因为 1 的最低有效位不为二。
-
3 的斜二进制是 10。因为 2 的最低有效位为二,向前进一。
-
4 的斜二进制是 11。因为 10 的最低有效位不为二。
-
5 的斜二进制是 12。因为 11 的最低有效位不为二。
-
6 的斜二进制是 20。因为 12 的最低有效位为二,向前进一。
-
7 的斜二进制是 100。因为 20 的最低有效位为二,向前进一。
-
8 的斜二进制是 101。因为 100 的最低有效位不为二。
-
9 的斜二进制是 102。因为 101 的最低有效位不为二。
-
10 的斜二进制是 110。因为 102 的最低有效位为二,向前进一。
-
11 的斜二进制是 111。因为 110 的最低有效位不为二。
-
12 的斜二进制是 112。因为 111 的最低有效位不为二。
-
13 的斜二进制是 120。因为 112 的最低有效位为二,向前进一。
总之大概就是这样。注意我们并不关心一个数的斜二进制是否唯一。
我们定义
我们考虑怎么递推这个
现在我们考虑怎么用求出的
int lca(int u, int v) {
if (d[u] < d[v]) swap(u, v);
while (d[u] > d[v])
if (d[lb[u]] < d[v])
u = fa[u];
else
u = lb[u];
while (u != v)
if (lb[u] == lb[v])
u = fa[u], v = fa[v];
else
u = lb[u], v = lb[v];
return u;
}
它的正确性是显而易见的,因为对于 while 的本质是相同的,即为从
我们以上面那个 while 为例:
while (d[u] > d[v])
if (d[lb[u]] < d[v])
u = fa[u];
else
u = lb[u];
在第一次走第一个 if 之前,
在第一次走第一个 if 之后,设每次走一次第一个 if 之前 if 都会带来 if 一定会使该有效位降低恰一位。而每至多两步第二个 if 就会让 if 后至多跟着一次第二个 if。而第一个 if 的总数量是
于是我们得到了一个