标签:
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 11320 Accepted Submission(s):
4119
LCA(Least Common Ancestors):即最近公共祖先,是指这样一个问题:在有根树中,找出某两个结点u和v的所有祖先中距离(u,v)最近的那个公共祖先(也就是离根最远的那个公共祖先)。
算法是基于DFS和并查集来实现的。
<具体结合遍历和并查集的归并的顺序理解,见图>
处理LCA(3,4):因为3在遍历到4之前先被访问到,所以LCA(3,4)=FindPar(par[3])=3;(此时对LCA(4,5)的询问操作暂被跳过。
处理LCA(5,4):因为4在遍历到5之被访问,所以LCA(5,4)=Find(par[4])=2
求树上两点间u,v的最短距离的方法:记dis[u]为u到根节点的距离
那么u到v之间的距离:ans[u][v]=dis[u]+dis[v]-2*dis[lca[u][v]](减去到根节点的公共距离的两倍);
题解:
1 #include<cstring> 2 #include<iostream> 3 using namespace std; 4 #include<cstdio> 5 #define M 201 6 #include<vector> 7 #define N 40100 8 vector <int> que[N];/*储存询问队列*/ 9 int ans[M];/*存着答案*/ 10 int n,m,u,v,k; 11 struct Edge{ 12 int v,last,w;/*边表*/ 13 }edge[N*2]; 14 int head[N],dis[N]={0}; 15 int T,t=0; 16 int father[N],ance[N];/*father[N]表示当前的处理的子树,ance代表这个点当前的祖先*/ 17 bool visit[N],root[N];/*visit判断当前的点的子树lca是否求过,root是寻找根节点*/ 18 void add_edge(int u,int v,int w) 19 { 20 ++t; 21 edge[t].v=v; 22 edge[t].w=w; 23 edge[t].last=head[u]; 24 head[u]=t; 25 } 26 void input() 27 { 28 memset(visit,false,sizeof(visit)); 29 memset(root,false,sizeof(root)); 30 memset(head,0,sizeof(head)); 31 memset(dis,0,sizeof(dis)); 32 memset(edge,0,sizeof(edge)); 33 scanf("%d%d",&n,&m); 34 for(int i=1;i<=n-1;++i) 35 { 36 scanf("%d%d%d",&u,&v,&k); 37 add_edge(u,v,k); 38 root[v]=true; 39 ance[i]=i;/*初始化*/ 40 father[i]=i; 41 } 42 for(int i=1;i<=m;++i) 43 { 44 scanf("%d%d",&u,&v); 45 que[u].push_back(i);/*因为离线算法求出结果是无法知道他的查询顺序的,但是我们要按照查询顺序输出,所以就在查询序列的偶数位存着下一位的在ans的顺序*/ 46 que[u].push_back(v); 47 que[v].push_back(i); 48 que[v].push_back(u); 49 } 50 } 51 int find(int x) 52 { 53 return (father[x]==x)?father[x]:father[x]=find(father[x]); 54 } 55 void tarjan(int k,int w) 56 { 57 ance[k]=k; 58 dis[k]=w; 59 for(int l=head[k];l;l=edge[l].last) 60 { 61 tarjan(edge[l].v,dis[k]+edge[l].w); 62 father[edge[l].v]=k;/*father存着当前点与全部的子树上的集合,同时把子树直接点的祖先设为k,所以查询一个子树上的点的区间的时候,要先用find找出代表元素,再求祖先*/ 63 ance[edge[l].v]=k; 64 } 65 visit[k]=true; 66 int size=que[k].size(); 67 for(int i=1;i<size;i+=2) 68 { 69 if(visit[que[k][i]])/*如果这条边的另一个点的lca已经求出来,那么这个点所在集合的祖先就是这两个点的最近公共祖先*/ 70 { 71 int zu=ance[find(que[k][i])]; 72 ans[que[k][i-1]]=dis[k]+dis[que[k][i]]-2*dis[zu]; 73 } 74 } 75 } 76 int main() 77 { 78 scanf("%d",&T); 79 while(T--) 80 { 81 input(); 82 for(int i=1;i<=n;++i) 83 { 84 if(!root[i])/*从根节点开始深搜*/ 85 { 86 tarjan(i,0); 87 break; 88 } 89 } 90 for(int i=1;i<=m;++i) 91 printf("%d\n",ans[i]); 92 } 93 return 0; 94 }
LCA(最近公共祖先)--tarjan离线算法 hdu 2586
标签:
原文地址:http://www.cnblogs.com/c1299401227/p/5499793.html