• 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 有兴趣再说

  • 相关阅读:
    30多条mysql数据库优化方法,千万级数据库记录查询轻松解决【转】
    安全快速修改Mysql数据库名的5种方法
    ASP.NET Web API 学习【转】
    前端学习必备知识
    为什么引用不了App_Code里的类(报“未能找到类型或命名空间名称”错误)
    【ASP.net】Equals 和 == 的区别
    ADO.NET完整的增、删、改、查
    面向对象--类库、委托、is和as运算符、泛型集合
    面向对象-构造函数和静态方法
    面向对象--多态、虚方法重写、抽象类、接口
  • 原文地址:https://www.cnblogs.com/NOI-AKer/p/Easy_LCA.html
Copyright © 2020-2023  润新知