LCA 倍增法
这里是求树上两点最近公共祖先的倍增求法。
倍增
简单来说,倍增就是:
1 , 2 , 4 , 8 , 16 , 32 , ⋯ 1,2,4,8,16,32,\cdots 1,2,4,8,16,32,⋯
像上面这个数列(其实准确来讲应该是思维),任何大于0的正整数,它都能够表示,这就是倍增。
倍增求LCA
这个点的祖先
不考虑如何求得那个祖先。考虑这个结点到祖先节点的路径,发现使用上面的倍增方法可以很好的描述(因为上面那个玩意可以很好的表达出这条路径长度嘛)。
考虑从小到大去表示这条路径,会发现很难描述用上面的方法如何去描述,但是从大到小去描述反而很简单(这个问题就像要求一个十进制的数字转化为二进制,方法是1248那样转化,是从小到大枚举可能的位置简单呢,还是从大到小枚举位置简单呢)。
因此,已经找到了一种快速找到这个结点的任意一个祖先的方法。
但是求两个点的祖先呢?
找两个结点的最近公共祖先
考虑两个深度一样的结点。如果我们可以按上面那种方式一起跳,不就可以一起遇到了嘛。然后这个问题就解决了。
但是更多的问题是两个结点深度不同。那想个法子把深度大的结点的深度缩一缩不久可以了嘛(当然如果有人愿意让那个深度小的结点跳到大的的话,也行(虽然可能还没到,这棵树就不够深了))。
问题又来了,我要怎么知道按那个数列的祖先呢?
即答:预处理呗(其实就是递推一下就好了)。具体如下。
vector<vector<int>>g;
const int MAXN = 1e6 + 10;
// 虽然这里是lg,但实际上是log2的预处理数组,具体为什么是log2看看上面那个数列化为2的指数形式
// fa数组的31个够多了,除非这棵树真的大(2^31不很大吗)
int fa[MAXN][31], depth[MAXN], lg[MAXN];
int n; //这颗树的结点数量
inline void pre() {
// 当然也可以用std::log2()函数,就是很慢
lg[1] = 0, lg[2] = 1;
for (int i = 3; i <= n; ++ i)
lg[i] = lg[i >> 1] + 1;
}
// 这里是预处理祖先
void dfs(int now, int dad