P3379 【模板】最近公共祖先(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。

很多人说这道题不能用倍增写

其实是可以的,只不过需要加一点读入优化罢了

注意倍增的最大值一定要取19而不能取20,因为这题卡常!!

代码比较简单,没有用位运算,也写了大量的注释并标明了易错点,大家可以参考一下

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 using namespace std;
 6 const int MAXN=1000001;
 7 int n,m,root;
 8 struct node
 9 {
10     int u;
11     int v;
12     int next;
13 }edge[MAXN];
14 int num=1;
15 int head[MAXN];
16 int deep[MAXN];
17 int f[MAXN][20];
18 void edge_add(int x,int y)
19 {
20     edge[num].u=x;
21     edge[num].v=y;
22     edge[num].next=head[x];
23     head[x]=num++;
24 }
25 void build_tree(int p)
26 {
27     for(int i=head[p];i!=-1;i=edge[i].next)// 遍历与p节点相邻的节点
28     {
29         int will=edge[i].v;
30         if(deep[will]==0)// 如果点will没有被访问过的话
31         {
32             deep[will]=deep[p]+1;// 则点will的深度==p的深度+1
33             f[will][0]=p;// will点向上跳2^0的节点是p
34             build_tree(will);//继续初始化will节点
35         }
36     }
37 }
38 void initialize_step()
39 {
40     for(int i=1;i<=19;i++)
41         for(int j=1;j<=n;j++)
42             f[j][i]=f[f[j][i-1]][i-1];
43     // 第j个节点,向上跳i能到达的节点就是 跳到i-1处再向上跳i-1能到达的节点
44     // 因为倍增是以次方的形式增加的
45 }
46 int LCA(int x,int y)
47 {
48     if(deep[x]<deep[y])swap(x,y);// 如果说节点x的深度比节点y的深度小的话,就交换他俩的位置,让x<y
49     for(int i=19;i>=0;i--)// 因为跳的步数越小越好,所以从最大的值开始跳
50     {
51         if(deep[f[x][i]]>=deep[y])// 如果跳完i步之后x还在y下方的话 ,这里必须加等于号
52         x=f[x][i];// 就更新x的值,继续跳
53     }
54     if(x==y)return y;//判断一下,如果x和y在同一条线上,就直接返回x的值 ,y也可以
55
56     for(int i=19;i>=0;i--)//再让x和y一起向上跳
57     if(f[x][i]!=f[y][i])
58     x=f[x][i],y=f[y][i];// 如果他们跳完之后的祖先不相等的话,就继续跳
59     return f[x][0];//按这样跳下去,一定会跳到只要再跳一步就能找到最近公共祖先的位置!
60 }
61 void read(int & x)
62 {
63     char c=getchar();x=0;
64     while(c<‘0‘||c>‘9‘)c=getchar();
65     while(c>=‘0‘&&c<=‘9‘)x=x*10+c-48,c=getchar();// 读入优化,必须要有!
66 }
67 int main()
68 {
69     //scanf("%d%d%d",&n,&m,&root);
70     read(n);read(m);read(root);
71     for(int i=1;i<=n;i++)head[i]=-1;
72     for(int i=1;i<=n-1;i++)
73     {
74         int x,y;
75         //scanf("%d%d",&x,&y);
76         read(x);read(y);
77         edge_add(x,y);
78         edge_add(y,x);
79     }
80     deep[root]=1;//将根节点的深度设为1
81     build_tree(root);// 建立起一棵树
82     initialize_step();// 初始化向上跳的距离
83     for(int i=1;i<=m;i++)
84     {
85         int x,y;
86         //scanf("%d%d",&x,&y);
87         read(x);read(y);// 求x与y的最近公共祖先
88         printf("%d\n",LCA(x,y));// ans
89     }
90     return 0;
91 } 
时间: 2024-10-21 00:15:58

P3379 【模板】最近公共祖先(LCA)的相关文章

[模板]最近公共祖先LCA

本人水平有限,题解不到为处,请多多谅解 本蒟蒻谢谢大家观看 题目:传送门 倍增求LCA模板 code: #include<bits/stdc++.h> #pragma GCC optimize(3) using namespace std; int n,q,a,b,tot,m; int nxt[1000010],head[1000010],ver[1000010],dep[1000010],f[1000010][21]; //设f[x,k]表示x的2^k辈祖先,即从x向根节点走2^k步到达的节

树链剖分 [模板]最近公共祖先LCA

本人水平有限,题解不到为处,请多多谅解 本蒟蒻谢谢大家观看 题目:传送门 树链剖分:跑两遍dfs,第一遍找重边,第二遍找重链. 重儿子:父亲节点的所有儿子中子树结点数目最多(size最大)的结点: 轻儿子:父亲节点中除了重儿子以外的儿子: 重边:父亲结点和重儿子连成的边: 轻边:父亲节点和轻儿子连成的边: 重链:由多条重边连接而成的路径: 轻链:由多条轻边连接而成的路径 son[]表示重儿子,top[]表示重链所在的第一个节点,sz[]表示子节点数,fa[]表示父亲节点 图示: code: #i

最近公共祖先(LCA)问题

描述 对于有根树T的两个节点u和v,最近公共祖先LCA(T,u,v)表示一个节点x满足x是u,v的公共祖先且x的深度尽可能大. 算法 求解LCA问题主要有三种解法,分别是暴力搜索,Tanjar算法,最后一种是转化为RMQ问题,用DFS+ST算法来求解 暴力搜索 如果数据量不大的时候可以采用暴力搜索法.先将节点u的祖先节点全部标记出来,然后顺着节点v沿着父亲节点的方向向上遍历,直到遍历到一个被标记的节点,这个节点即为所求节点.或者分别获取u,v到根节点的路径P1,P2,可以将这两条路径看做两个两个

POJ 1470 Closest Common Ancestors【最近公共祖先LCA】

题目链接:http://poj.org/problem?id=1470 题目大意:给出一棵树,再给出若干组数(a,b),输出节点a和节点b的最近公共祖先(LCA) 就是很裸的LCA,但是我用的是<挑战程序设计竞赛>上的"基于二分搜索的算法求LCA",我看网上用的都是tarjan算法.但是我的代码不知道为什么提交上去 wrong answer,自己想的很多测试数据也都和题解结果一样,不知道错在哪里,所以把代码保存一下,留待以后解决...... 如果读者有什么建议,希望提出来,

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先

【C++】最近公共祖先LCA(Tarjan离线算法)&amp;&amp; 洛谷P3379LCA模板

1.前言 首先我们介绍的算法是LCA问题中的离线算法-Tarjan算法,该算法采用DFS+并查集,再看此算法之前首先你得知道并查集(尽管我相信你如果知道这个的话肯定是知道并查集的),Tarjan算法的优点在于相对稳定,时间复杂度也比较居中,也很容易理解(个人认为). 2.思想 下面详细介绍一下Tarjan算法的思想: 1.任选一个点为根节点,从根节点开始. 2.遍历该点u所有子节点v,并标记这些子节点v已被访问过. 3.若是v还有子节点,返回2,否则下一步. 4.合并v到u上. 5.寻找与当前点

HDU 2586 How far away ?(LCA模板 近期公共祖先啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house

HDU 2586 How far away ?(LCA模板 最近公共祖先啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house

最近公共祖先LCA【模板】

Tarjan-LCA算法: 对于每一点u: 1.建立以u为代表元素的集合. 2.遍历与u相连的节点v,如果没有被访问过,对于v使用Tarjan-LCA算法,结束后,将v的集合并入u的集合. 3.对于与节点u相关的询问(u,v),如果v被访问过,则结果就是v所在集合的所代表的元素. 求(u,v)的最近公共祖先节点,则询问时调用QEdges[k].lca = find(QEdges[k].to); 求(u,v)在树上的距离,Dist(u,v) = Dist(1,u) + Dist(1,v) - 2*

最近公共祖先lca模板

void dfs(int x,int root){//预处理fa和dep数组 fa[x][0]=root; dep[x]=dep[root]+1; for(int i=1;(1<<i)<dep[x];i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for(int i=0;i<ve[x].size();i++)dfs(ve[x][i],x); } int lca(int a,int b){//计算两个节点的最近公共祖先 if(dep[a]>dep[b])s