码迷,mamicode.com
首页 > 其他好文 > 详细

LCA--P3379 【模板】最近公共祖先(LCA)

时间:2020-02-01 21:10:47      阅读:74      评论:0      收藏:0      [点我收藏+]

标签:lin   数组   一个   RKE   minus   ase   span   name   names   

 

题目描述

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

输入格式

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

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

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

输出格式

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

 

设 f [ u ][ k ] 表示 u 的 2k 辈祖先,即从 u 向根节点走 2k 步到达的节点。特别地,若该节点不存在,则令 f [ u ][ k ] = 0 。f [ u ][ 0 ] 就是 x 的父节点。因为 u 向根节点走 2k ⇔ 向根节点走 2k-1 步,再走 2k-1 步。所以对于 k∈ [ 1,logn ] ,有 f [ u ][ k ] = f [ f [ u ][ k-1 ] ][ k-1 ]。

f 数组利用了递推的思想。递推式为: f [ u ][ k ] = f [ f [ u ][ k-1 ] ][ k-1 ]。因此,我们可以对树进行遍历 DFS ,由此得到 f [ u ][ 0 ],再计算出 f 数组的所有值。

计算 LCA( x, y ) 分为以下几步:

①设 dep [ x ] 表示 x 的深度。那么设 dep [ x ] ≥ dep [ y ] 。(否则,可交换 x, y )

②利用二进制拆分的思想,把 x 向上调整到与 y 同一的深度。即:依次尝试从 x 向上走 k = 2logn… 21,20 步,若到达的点比 y 深,则令 x = f [ x ][ k ]。

③若此时的 x = y ,则说明已经找到了 LCA ,两点的 LCA 就等于 y 。

④若此时的 x ≠ y ,那么 x, y 同时向上调整,并保持深度一致且二者不会相会。具体来说就是,依次尝试把 x, y 同时向上走 k = 2logn… 21,20 步,若 f [ x ][ k ] ≠ f [ y ][ k ](即仍未相会),则令 x = f [ x ][ k ],y = f [ y ][ k ]。

⑤此时 x,y 必定只差一步就相会了,他们的父节点 f [ x ][ 0 ] 就是 LCA。

 1 #include<cstdio>
 2 #include<cmath>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<queue>
 7 using namespace std;
 8 #define maxn 501000
 9 int n,m,s;
10 int dep[maxn<<1];
11 int f[maxn<<1][21];
12 int head[maxn<<1],cnt=0;
13 struct hh
14 {
15     int nex,to;
16 }t[maxn<<1];
17 inline void add(int nex,int to)
18 {
19     t[++cnt].nex=head[nex];
20     t[cnt].to=to;
21     head[nex]=cnt;
22 }
23 inline void Deal_first(int u,int fa)
24 {
25     dep[u]=dep[fa]+1;
26     for(int i=0;i<20;i++)
27         f[u][i+1]=f[f[u][i]][i];
28     for(int i=head[u];i;i=t[i].nex)
29     {
30         int v=t[i].to;
31         if(v==fa) continue;
32         f[v][0]=u;
33         Deal_first(v,u);
34     }
35 return;
36 }
37 inline int LCA(int x,int y)
38 {
39     if(dep[x]<dep[y]) swap(x,y);
40     for(int i=20;i>=0;i--)
41     {
42         if(dep[f[x][i]]>=dep[y]) x=f[x][i];
43         if(x==y) return x;
44     }
45     for(int i=20;i>=0;i--)
46     {
47         if(f[x][i]!=f[y][i])
48         {
49             x=f[x][i];
50             y=f[y][i];
51         }
52     }
53     return f[x][0];
54 }
55 inline int read()
56 {
57     int kr=1,xs=0;
58     char ls;
59     ls=getchar();
60     while(!isdigit(ls))
61     {
62         if(!(ls^45))
63             kr=-1;
64         ls=getchar();
65     }
66     while(isdigit(ls))
67     {
68         xs=(xs<<1)+(xs<<3)+(ls^48);
69         ls=getchar();
70     }
71     return xs*kr;
72 }
73 int main()
74 {
75     int x,y;
76     n=read();m=read();s=read();
77     for(int i=1;i<n;i++)
78     {
79         x=read();y=read();
80         add(x,y);
81         add(y,x);
82     }
83     Deal_first(s,0);
84     for(int i=1;i<=m;i++)
85     {
86         x=read();y=read();
87         printf("%d\n",LCA(x,y));
88     }
89 return 0;
90 }

 

LCA--P3379 【模板】最近公共祖先(LCA)

标签:lin   数组   一个   RKE   minus   ase   span   name   names   

原文地址:https://www.cnblogs.com/very-beginning/p/12249881.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!