BSOJ1620 -- 【LCA练习】最近公共祖先(版本2)3587

类似于luogu P3379 【模板】最近公共祖先(LCA)

Description

  给你一棵有根树,要求你计算出指定两个结点的最近公共祖先。

Input

  输入文件的第一行为结点个数n(2<=N<=200,000),结点编号为1到n
  接下来n-1行,每行两个整数,第一个数字是第二个数字的父亲结点
  接下来的一行为两个整数a和b,要求计算出结点a和b的最近公共祖先。

Output

  输出文件仅一行为最近公共祖先的编号。

Sample Input

5 2 3 3 4 3 1 1 5 3 5

Sample Output

3

爬树法(树上倍增):
动态做法,边输入边找树根(套路)
 
预处理出p[i][k]代表i的第k代祖先,d[i]代表节点i的深度
之后用LCA爬树法重点处理:
  1)使d[L]>d[R],否则交换,方便后面处理
  2)计算出最多跳跃深度k=log2(d[L])
  3)L跳到与R深度相同
  4)L,R同时跳到LCA的子节点处
  5)计算出LCA
 
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define why 200005
struct starr
{
    int next,to;
}a[why<<1];
int n,root,p[why][18],d[why],prt[why],h[why],cnt;
inline void add(int x,int y)
{
    cnt++;
    a[cnt].to=y;
    a[cnt].next=h[x];
    h[x]=cnt;
}
inline void DFS(int x,int num)
{
    register int u;
    d[x]=num;
    for(u=h[x];u;u=a[u].next)
    {
        int y=a[u].to;
        DFS(y,num+1);
    }
}
inline void ST()
{
    register int i,j;
    for(i=1;i<=n;i++)p[i][0]=prt[i];
    for(j=1;(1<<j)<=n;j++)
    {
        for(i=1;i<=n;i++)
        {
            if(p[i][j-1]==-1)continue;
            p[i][j]=p[p[i][j-1]][j-1];
        }
    }
}
inline int LCA(int L,int R)
{
    register int i;
    int k;
    if(L==R)return L;
    if(d[L]<d[R])swap(L,R);
    k=int(log(d[L])/log(2));
    for(i=k;i>=0;i--)
    {
        if(d[L]-(1<<i)>d[R])L=p[L][i];
        else if(d[L]-(1<<i)==d[R])
        {
            L=p[L][i];
            break;
        }
    }
    if(L==R)return L;
    for(i=k;i>=0;i--)
    {
        if(p[L][i]!=-1&&p[L][i]!=p[R][i])
        {
            L=p[L][i];
            R=p[R][i];
        }
    }
    return p[L][0];
}
inline int redn()
{
    int ret=0,f=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')f=-f;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        ret=ret*10+ch-'0';
        ch=getchar();
    }
    return f>0?ret:-ret;
}
int main()
{
    int _1,_2;
    register int i;
    n=redn();
    memset(p,-1,sizeof(p));
    for(i=1;i<n;i++)
    {
        _1=redn();
        _2=redn();
        prt[_2]=_1;
        add(_1,_2);
        if(i==1||_2==root)root=_1;
    }
    DFS(root,1);
    ST();
    _1=redn();
    _2=redn();
    printf("%d",LCA(_1,_2));
    return 0;
}
 

Tarjan离线求LCA(待填)

RMQ/LCT求LCA 有兴趣再说

原文地址:https://www.cnblogs.com/NOI-AKer/p/Easy_LCA.html