「LuoguP3379」 【模板】最近公共祖先(LCA)

题目描述

如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入输出格式

输入格式:

第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。

接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。

输出格式:

输出包含M行,每行包含一个正整数,依次为每一个询问的结果。

输入输出样例

输入样例#1: 复制
5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
输出样例#1: 复制
4
4
1
4
4

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=10,M<=10

对于70%的数据:N<=10000,M<=10000

对于100%的数据:N<=500000,M<=500000

样例说明:

该树结构如下:

第一次询问:2、4的最近公共祖先,故为4。

第二次询问:3、2的最近公共祖先,故为4。

第三次询问:3、5的最近公共祖先,故为1。

第四次询问:1、2的最近公共祖先,故为4。

第五次询问:4、5的最近公共祖先,故为4。

故输出依次为4、4、1、4、4。


题解

其实是放一下代码

众所周知,LCA有几种常见的做法

  • 暴力跳
    • 先把较深的往上跳,跳到同一深度
    • 然后一起跳
    • 单次复杂度$O(n)$分分钟带你上天
  • 倍增
    • 在跳的时候优化一下,不一格一格的跳,而是拆分成二进制跳
    • 需要预处理出每个点往上$2^i$步的祖先,
    • 时间复杂度$O(nlogn+mlogn)$,空间复杂度$O(nlogn)$
  •  1 /*
     2     qwerta
     3     P3379 【模板】最近公共祖先(LCA)
     4     Accepted
     5     100
     6     代码 C++,1.37KB
     7     提交时间 2018-03-13 18:33:35
     8     耗时/内存
     9     1672ms, 51789KB
    10 */
    11 #include<iostream>
    12 #include<cstdio>
    13 #include<algorithm>
    14 using namespace std;
    15 struct emm{
    16     int f,e;
    17 }a[1000007];
    18 int h[500007];
    19 int d[500007];
    20 int p[500007][20];
    21 void dfs(int no,int fa)
    22 {
    23     d[no]=d[fa]+1;
    24     //cout<<no<<" "<<d[no]<<" "<<fa<<endl;
    25     p[no][0]=fa;
    26     int w;
    27     for(w=1;w<20;w++)
    28     p[no][w]=p[p[no][w-1]][w-1];
    29     for(w=h[no];w;w=a[w].f)
    30     {
    31         if(a[w].e!=fa)
    32         dfs(a[w].e,no);
    33     }
    34     return;
    35 }
    36 int main()
    37 {
    38     int c=0,x,y,n,m,s,i,j;
    39     scanf("%d%d%d",&n,&m,&s);
    40     for(i=1;i<n;++i)
    41     {
    42         scanf("%d%d",&x,&y);
    43         ++c;
    44         a[c].f=h[x];
    45         h[x]=c;
    46         a[c].e=y;
    47         ++c;
    48         a[c].f=h[y];
    49         h[y]=c;
    50         a[c].e=x;
    51         d[i]=99999999;
    52     }
    53     d[n]=99999999;
    54     dfs(s,0);
    55     for(i=1;i<=m;++i)
    56     {
    57         scanf("%d%d",&x,&y);
    58         if(d[x]<d[y])swap(x,y);
    59         for(j=19;j>=0;--j)
    60         {
    61             if((d[x]-d[y])>=(1<<j))
    62             {
    63                 x=p[x][j];
    64                 //cout<<x<<" ";
    65             }
    66         }
    67         if(x==y)printf("%d\n",x);
    68         else{
    69         for(j=19;j>=0;--j)
    70         {
    71             if(p[x][j]!=p[y][j])
    72             {
    73                 x=p[x][j];
    74                 y=p[y][j];
    75             }
    76         }
    77         printf("%d\n",p[x][0]);}
    78     }
    79     /*
    80     for(i=1;i<=n;i++)
    81     {
    82     cout<<i<<" ";
    83     for(j=0;j<=19;j++)
    84     cout<<p[i][j]<<" ";
    85     cout<<endl;
    86     }*/
    87     return 0;
    88 }
    倍增求LCA
  • ST表
    • 原理:dfs序在这两点之间的点中,深度最小的点为lca
    • 所以记录dfs序

猜你喜欢

转载自www.cnblogs.com/qwerta/p/9762958.html