【简介】
并查集在一次DFS(深度优先遍历)中完成所有询问。换句话说,要所有询问都读入后才开始计算,所以是一种离线的算法。
【原理】
后序遍历的时候,必然先访问完x的所有子树,其中包含u、v,然后才会返回到x所在的节点。这个性质就是我们使用Tarjan算法解决最近公共祖先问题的核心思想。
关键路径P ,已遍历的点位于路径P中某个点的子树中,当遍历到u时v已遍历过(u的子树已遍历完),那么v必然存在于子树pk中,此时LCA(u,v)就等于现在v所在集合的祖先pk。如果还没有遍历到,则继续遍历,只不过LCA(u,v)要等到遍历到v时才能知道了,原理如上。需要注意的一点是,为了保持上图的性质,如果一个节点的一个子树遍历完了,需要合并该节点的子树集合。
tarjan算法的步骤是(当dfs到节点u时):
(一) 在并查集中建立仅有u的集合,设置该集合的祖先为u
(二) 对u的每个孩子v:
1. tarjan之
2. 合并v到父节点u的集合,确保集合的祖先是u
(三)设置u为已遍历
(四)处理关于u的查询,若查询(u,v)中的v已遍历过,则LCA(u,v)= v所在的集合的祖先
【举例】
假设遍历完10的孩子,要处理关于10的请求了 可以发现集合的祖先便是LCA ! |
【HDU 2586】
换成Tarjan 离线算法来做。
1 #pragma comment(linker, "/STACK:1024000000,1024000000")
2 #include <stdio.h>
3 #include <string.h>
4 #include <vector>
5 #include <cmath>
6 using namespace std;
7 int n,m;
8 struct edge
9 {
10 int d,v,next;
11 edge(){}
12 edge(int _d,int _v,int _next)
13 {
14 d=_d;v=_v;next=_next;
15 }
16 }data[80003];
17 int map[40003];
18 int pool;
19 void addedge(int s,int e,int v)
20 {
21 int t=map[s];
22 data[pool++]=edge(e,v,t);
23 map[s]=pool-1;
24 }
25 int mset[40003];
26 int find(int k)
27 {
28 if (mset[k]==-1) return k;
29 return mset[k]=find(mset[k]);
30 }
31 void uion(int a,int b)
32 {
33 int aa=find(a);
34 int bb=find(b);
35 mset[aa]=bb;
36 }
37 struct _que
38 {
39 int a,b;
40 _que(int q=0,int w=0){a=q;b=w;}
41 };
42 vector<vector<_que> > ques;
43 vector<int > ans;
44 int ifv[40003];
45 int dis[40003];
46 int anc[40003];
47 void tar(int cur)
48 {
49 ifv[cur]=1;
50 anc[cur]=cur;
51 int p=map[cur];
52 while (p!=-1)
53 {
54 if (!ifv[data[p].d])
55 {
56 dis[data[p].d]=dis[cur]+data[p].v;
57 tar(data[p].d);
58 uion(cur,data[p].d);
59 anc[find(cur)]=cur;
60 }
61 p=data[p].next;
62 }
63 ifv[cur]=2;
64 for (int i=0;i<(int)ques[cur].size();++i)
65 {
66 if (ifv[ques[cur][i].a]==2)
67 ans[ques[cur][i].b]=dis[cur]+dis[ques[cur][i].a]-2*dis[anc[find(ques[cur][i].a)]];
68 }
69 }
70 int main()
71 {
72 int T;
73 scanf("%d",&T);
74 while (T--)
75 {
76 ques.clear();
77 pool=0;
78 memset(map,-1,sizeof map);
79 memset(ifv,0,sizeof ifv);
80 memset(mset,-1,sizeof mset);
81 scanf("%d%d",&n,&m);
82 ques.resize(n);
83 int s,e,v;
84 for (int i=0;i<n-1;++i)
85 {
86 scanf("%d%d%d",&s,&e,&v);
87 addedge(s-1,e-1,v);
88 addedge(e-1,s-1,v);
89 }
90 dis[0]=0;
91 ans.resize(m);
92 for (int i=0;i<m;++i)
93 {
94 int u,v;
95 scanf("%d%d",&u,&v);
96 --u;--v;
97 ques[u].push_back(_que(v,i));
98 ques[v].push_back(_que(u,i));
99 }
100 tar(0);
101 for (int i=0;i<(int)ans.size();++i)
102 {
103 printf("%d\n",ans[i]);
104 }
105 }
106 }
View Code