LCA--最近公共祖先

来源:互联网 发布:js select foreach 编辑:程序博客网 时间:2024/05/16 10:23

http://acm.hdu.edu.cn/showproblem.php?pid=2586

问题概述:有一棵含有n个点的树,每个边都有一个权值,之后对于每次查询(a,b)求出从a到b边的长

输入样例:                                                  对应输出:

2                                                                  10

3 2                                                               25

1 2 10

3 1 15

1 2

2 3


离线求解:将所有的查询记录下来,之后通过遍历整棵树来求出所有查询的答案

主要算法:BFS+并查集

flag[k]:k点是否已经遍历; len[k]:该点与根的距离; ans[i]:用来存第i次查询的答案,其中a点与b点的距离

ans[i]==len[a]+len[b]-2*len[a与b的公共祖先]

解题过程:

①:从根开始搜索,搜索到每个点标记flag[k]=1,遍历它所有的子节点

②:当遍历完k的其中一个子节点之后,将这个点与k加入并查集

③:当遍历完k所有的子节点之后,判定有没有端点为k的边要查询,若有,则判定这条边另外一个端点temp是否已

经被遍历过,符合条件的话这个边的长度就可以用上面的公式求出,其中k与temp的公共祖先就是Find(temp)!


#include<stdio.h>#include<string.h>#include<vector>using namespace std;typedef struct{int y;int val;}Line;Line now;int n, ans[205], ufs[44444], flag[44444], len[44444];vector<Line> v[44444], q[44444];void Trajan(int x, int val);int Find(int x){if(ufs[x]==-1)return x;return ufs[x] = Find(ufs[x]);}int main(void){int T, i, m, a, b, c;scanf("%d", &T);while(T--){scanf("%d%d", &n, &m);for(i=1;i<=n;i++)v[i].clear(), q[i].clear();memset(ufs, -1, sizeof(ufs));memset(flag, 0, sizeof(flag));memset(len, 0, sizeof(len));for(i=1;i<n;i++){scanf("%d%d%d", &a, &b, &c);now.y = b, now.val = c;v[a].push_back(now);now.y = a;v[b].push_back(now);}for(i=1;i<=m;i++){scanf("%d%d", &a, &b);now.y = b, now.val = i;q[a].push_back(now);now.y = a;q[b].push_back(now);}Trajan(1,0);for(i=1;i<=m;i++)printf("%d\n", ans[i]);}return 0;}void Trajan(int x, int val){int i, temp, t1, t2;flag[x] = 1;len[x] = val;for(i=0;i<v[x].size();i++){temp = v[x][i].y;if(flag[temp]==1)continue;Trajan(temp, val+v[x][i].val);t1 = Find(x);/*注意顺序,否则下面的Find(temp)就会因为路径压缩导致错误*/t2 = Find(temp);if(t1!=t2)ufs[t2] = t1;}for(i=0;i<q[x].size();i++){temp = q[x][i].y;if(flag[temp]==1){//printf("%d %d %d\n", x, temp, Find(temp));ans[q[x][i].val] = len[x]+len[temp]-2*len[Find(temp)];}}}



1 0
原创粉丝点击