[模板] LCA-最近公共祖先-倍增法
2019-11-07 09:25:45
C.树之呼吸-叁之型-树上两点路径长度 | |||||
|
|||||
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 **/