[模板] LCA-最近公共祖先-倍增法

2019-11-07 09:25:45

 

C.树之呼吸-叁之型-树上两点路径长度
Time Limit: 1000 MS Memory Limit: 32768 K
Total Submit: 7 (4 users) Total Accepted: 2 (2 users) Special Judge: No
Description

给一棵 n 个结点的树,结点编号从 1 到 n,并指定 m 号结点为根;

给出 q 个询问,每次询问从编号为 x 的结点到编号为 y 的结点的路径长度。

Input

输入第一行为一个正整数 T,表示测试数据组数;

对于每组测试数据,输入第一行为三个正整数 n、m、q,表示树的结点数,根结点的编号以及询问数;

接下来 n - 1 行给出树的结构,每行两个正整数 x、y,表示结点 x 与结点 y 有边相连;

接下来 q 行给出询问,每行两个正整数 x、y,表示询问从结点 x 到结点 y 的路径长度;

1 <= T <= 20,1 <= n,q <= 1e5,1 <= m <= n。

Output

每组测试数据的第一行输出“Case #i:”(不含引号),表示是第 i 组测试数据;

接下来 q 行,每行输出一个整数,表示给出的两结点间的路径长路。

Sample Input
2
5 2 3
1 4
5 4
2 4
3 2
1 3
5 2
4 1
3 1 2
1 2
1 3
1 1
2 3
Sample Output
Case #1:
3
2
1
Case #2:
0
2
Author
陈鑫

最近公共祖先LCA模板题,

ans=deep[x]-deep[LCA(x,y)]+deep[y]-deep[LCA(x,y)];

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int amn=2e5+5;
 4 vector<int> eg[amn];
 5 int n,m,maxh,deep[amn],anc[amn][20];    ///maxh为树的最大深度=(int)log2(n)+1,deep为结点的深度,anc为距离结点x路径长度为2^i的结点是哪个
 6 queue<int> q;
 7 
 8 void bfs(int rt){
 9     q.push(rt);deep[rt]=1;
10     while(q.size()){
11         int u=q.front();q.pop();
12         for(int i=0;i<eg[u].size();i++){
13             int v=eg[u][i];
14             if(deep[v])continue;
15             deep[v]=deep[u]+1;      ///深度
16             anc[v][0]=u;
17             for(int j=1;j<=maxh;j++) anc[v][j]=anc[anc[v][j-1]][j-1];   ///预处理2^k距离
18             q.push(v);
19         }
20     }
21 }
22 int Lca(int x,int y){
23     if(deep[x]<deep[y])swap(x,y);   ///我们规定x的深度要比y的深度大,所有若x的深度比y的深度小则要将他们交换
24     for(int i=maxh;i>=0;i--) if(deep[anc[x][i]]>=deep[y])x=anc[x][i];   ///将x和y移到同一深度,为什么是>=,因为anc[x][i]表示距离x结点2^i路径长度的结点时哪个,如果时anc[x][0]则表示距离x结点路径长度为1的结点
25     if(x==y)return x;   ///如果x==y说明y是他们的LCA,现在x==y了,可以任意返回x或y,这里你返回y也行
26     for(int i=maxh;i>=0;i--) if(anc[x][i]!=anc[y][i]){x=anc[x][i];y=anc[y][i];} ///找x和y的LCA
27     return anc[x][0];   ///输出LCA
28 }
29 void init(int rt){
30     memset(deep,0,sizeof deep);
31     memset(anc,0,sizeof anc);
32     maxh=(int)log2(n)+1;
33     bfs(rt);
34 }
35 int main(){
36     int T,Case=1,an,x,y,q;scanf("%d",&T);
37     while(T--){
38         for(int i=1;i<=n;i++)eg[i].clear(); ///用vector存边要加这句
39         scanf("%d%d%d",&n,&m,&q);
40         for(int i=1;i<n;i++){
41             scanf("%d%d",&x,&y);
42             eg[x].push_back(y);
43             eg[y].push_back(x);
44         }
45         init(m);
46         printf("Case #%d:\n",Case++);
47         while(q--){
48             scanf("%d%d",&x,&y);
49             an=Lca(x,y);
50             printf("%d\n",deep[x]+deep[y]-2*deep[an]);  ///(deep[x]-deep[an])+(deep[y]-deep[an]),x到最近公共祖先的深度+y到最近公共祖先的深度
51         }
52     }
53 }
54 /**
55 题意: 给一棵 n 个结点的树,结点编号从 1 到 n,并指定 m 号结点为根;
56 给出 q 个询问,每次询问从编号为 x 的结点到编号为 y 的结点的路径长度。
57 思路:求出x和y的最近公共祖先,答案是x到最近公共祖先的深度+y到最近公共祖先的深度
58 **/

 

 

 

 

posted @ 2019-11-07 09:28  Railgun000  阅读(188)  评论(0编辑  收藏  举报