树链剖分求LCA(超级快)

学习树链剖分以后,可用于求LCA,居然比倍增大法快上一倍!!!

最后一发是树链剖分老婆!!!tql,上模板题传送门

#include <bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T& t)
{
    int f = 0, c = getchar();
    t = 0;
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) t = t * 10 + c - 48, c = getchar();
    if (f) t = -t;
}
const int N = 5e5 + 10;
int cnt, head[N];
struct node
{
    int to, nex;
} e[N << 1];
inline void add_edge(int u, int v)
{
    e[++cnt].to = v;
    e[cnt].nex = head[u];
    head[u] = cnt;
}
int f[N], son[N], dep[N], siz[N], top[N];
//处理父亲 重儿子 深度
void dfs1(int u, int fa)
{
    f[u] = fa, siz[u] = 1, dep[u] = dep[fa] + 1;
    int mx = -1;
    for(int i = head[u]; i; i = e[i].nex)
    {
        int v = e[i].to;
        if(v == fa) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if(siz[v] > mx)
        {
            mx = siz[v];
            son[u] = v;
        }
    }
}
//树链剖分 处理top
void dfs2(int u, int t)
{
    top[u] = t;
    if(!son[u]) return; //没有重儿子代表是叶子节点
    dfs2(son[u], t); //一直朝重儿子走 剖重链
    for(int i = head[u]; i; i = e[i].nex)
    {
        int v = e[i].to;
        if(v == f[u] || v == son[u]) continue;
        dfs2(v, v); //另外开始一条链
    }
}
//除根节点外任何一个结点的父亲节点一定在一条重链上
//因为父亲节点存在儿子 所以一定存在重儿子 一定在一条重链上
inline int lca(int u, int v)
{
    while(top[u] != top[v]) //如果不在同一条重链上
    {
        //顶部节点深度较大得为u 一直往上跳
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        u = f[top[u]];
    }
    return dep[u] < dep[v] ? u : v;
}
int n, m, rt;
signed main()
{
    read(n), read(m), read(rt);
    int x, y;
    for(int i = 1; i < n; ++i)
    {
        read(x), read(y);
        add_edge(x, y);
        add_edge(y, x);
    }
    dfs1(rt, rt);
    dfs2(rt, rt);
    while(m--)
    {
        read(x), read(y);
        printf("%d\n", lca(x, y));
    }
    return 0;
}
posted @ 2022-06-30 09:10  std&ice  阅读(133)  评论(0)    收藏  举报