倍增lca的另类做法(以空间换取小常数)

陈锋老师讲到的倍增lca的另类做法。

众所周知,可以利用利用dfs序判断一个点y是否在点x的子树中(不再赘述)。

于是在寻找x和y的lca的时候,只需要让深度小的那个点(y)往上倍增爬,直到y的父亲是x的祖先。

这个原理上比传统算法快了一倍,但是占用了较多的内存。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iomanip>
#include <cmath>
#include <queue>
#include <map>
using namespace std;
#define reg register
#define LL long long
inline LL read() {
    LL res=0;char ch=getchar();bool fu=0;
    while(!isdigit(ch)) fu|=(ch=='-'), ch=getchar();
    while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return fu ? -res : res;
}
#define mkp make_pair
#define pii pair<int, int>

namespace BriMon
{
#define N 500005
int n, m, root;
struct edge {
    int nxt, to;
}ed[N<<1];
int head[N], cnt;
inline void add(int x, int y) {
    ed[++cnt] = (edge){head[x], y};
    head[x] = cnt;
}
int fa[N][21], dep[N];
int in[N], out[N], dfsc;

bool isancestor(int x, int y)  //x是不是y的祖先 
{
    return (in[x] <= in[y]) and (out[x] >= out[y]);
}

inline int lca(int x, int y) {
    if (dep[x] < dep[y]) swap(x, y);
    if (isancestor(y, x)) return y;
    for (reg int i = 20 ; i >= 0 ; i --) if (fa[y][i] and !isancestor(fa[y][i], x)) y = fa[y][i];
    return fa[y][0];
}

void dfs(int x) 
{
    in[x] = ++dfsc;
    for (reg int i = head[x] ; i ; i = ed[i].nxt) {
        int to = ed[i].to;
        if (dep[to]) continue;
        dep[to] = dep[x] + 1;
        fa[to][0] = x;
        for (reg int j = 1 ; j <= 20 ; j ++) fa[to][j] = fa[fa[to][j-1]][j - 1];
        dfs(to);
    }
    out[x] = ++dfsc;
}

signed main() 
{
    n = read(), m = read(), root = read();
    for (reg int i = 1 ; i < n ; i ++)
    {
        int x = read(), y = read();
        add(x, y), add(y, x);
    }
    dep[root] = 1;
    dfs(root);
    while(m--) printf("%d\n", lca(read(), read()));
    return 0;
}

}

signed main()
{
    BriMon :: main();
    return 0;
}

 

posted @ 2020-10-02 11:43  zZhBr  阅读(160)  评论(0编辑  收藏  举报