hdu 2586 How far away ? (LCA)

How far away ?

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4244    Accepted Submission(s): 1617


Problem Description
There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B"? Usually it hard to answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path("simple" means you can't visit a place twice) between every two houses. Yout task is to answer all these curious people.
 

 

Input
First line is a single integer T(T<=10), indicating the number of test cases.
  For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n.
  Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.
 

 

Output
For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.
 

 

Sample Input
2
3 2
1 2 10
3 1 15
1 2
2 3
 
 
2 2
1 2 100
1 2
2 1
 

 

Sample Output
10
25
100
100
 

 

Source
 

 

Recommend
lcy   |   We have carefully selected several similar problems for you:  3486 2874 2888 3234 2818 
 

 参考: http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html

这篇博客写的非常不错,我就是看这个学会的。

第一次写最近公共祖先问题,用的邻接表指针。

对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点。

       0

       |

       1

     /   \

   2      3

比如说在这里,如果0为根的话,那么1是2和3的父亲结点,0是1的父亲结点,0和1都是2和3的公共祖先结点,但是1才是最近的公共祖先结点,或者说1是2和3的所有祖先结点中距离根结点最远的祖先结点。

在求解最近公共祖先为问题上,用到的是Tarjan的思想,从根结点开始形成一棵深搜树,非常好的处理技巧就是在回溯到结点u的时候,u的子树已经遍历,这时候才把u结点放入合并集合中,这样u结点和所有u的子树中的结点的最近公共祖先就是u了,u和还未遍历的所有u的兄弟结点及子树中的最近公共祖先就是u的父亲结点。以此类推。。这样我们在对树深度遍历的时候就很自然的将树中的结点分成若干的集合,两个集合中的所属不同集合的任意一对顶点的公共祖先都是相同的,也就是说这两个集合的最近公共最先只有一个。对于每个集合而言可以用并查集来优化,时间复杂度就大大降低了,为O(n + q),n为总结点数,q为询问结点对数。

 

 

算法名称

针对问题

时间消耗

空间消耗

ST算法

一般RMQ问题

O(Nlog2N)-O(1)

O(Nlog2N)

Tarjan算法

LCA问题

O(Na(N) + Q)

O(N)

±1RMQ算法

±1RMQ问题

O(N)-O(1)

O(N)

不懂算法的可以找棵树模拟一下递归的过程。

给出Tarjan离线算法的做法:

 1 //62MS    4232K    1750 B    G++
 2 /*
 3 
 4     第一题LCA:
 5         在看完算法后发现还未能完全实现,因为理解还不透彻,所以想先做
 6     一题然后再从题入手。看了别人的结题报告后才写的,感觉还好。
 7     这一题就是模板题了。这里采用的是离线算法Tarjan。 
 8      
 9      树两点的间的最近距离=根节点分别到两点间的最近距离-2*根节点到两点的LCA距离。 
10          
11          ans = dis[u] + dis[v] - 2 * dis[lca(v, v)] 
12 
13 */
14 #include<iostream>
15 #include<queue>
16 #include<vector>
17 #define N 40005
18 using namespace std;
19 struct node{
20     int v;
21     int d;
22     node(int a,int b){
23         v=a;d=b;
24     }
25 };
26 vector<node>child[N],V[N]; //分别记录树和询问的邻接表 
27 int set[N]; 
28 int vis[N];
29 int dis[N];
30 int res[205][3]; //res[i][0]=u;res[i][1]=v;res[i][2]=LCA(u,v); 
31 int n;
32 void init()
33 {
34     for(int i=0;i<=n;i++){ 
35         child[i].clear();
36         V[i].clear();
37     }
38     memset(vis,0,sizeof(vis));
39     memset(dis,0,sizeof(dis));
40     memset(res,0,sizeof(res));
41 }
42 int find(int x)
43 {
44     if(set[x]!=x) set[x]=find(set[x]);
45     return set[x];
46 }
47 void merge(int x,int y)
48 {
49     int x0=find(x);
50     int y0=find(y);
51     set[y0]=x0;
52 } 
53 void LCA(int u) //tarjan算法 
54 {
55     set[u]=u;
56     vis[u]=true;
57     for(int i=0;i<V[u].size();i++){
58         if(vis[V[u][i].v]){
59             res[V[u][i].d][2]=find(V[u][i].v);
60         }
61     }
62     for(int i=0;i<child[u].size();i++){
63         if(!vis[child[u][i].v]){
64             dis[child[u][i].v]=dis[u]+child[u][i].d;
65             LCA(child[u][i].v);
66             merge(u,child[u][i].v);
67             set[child[u][i].v]=u;
68         }
69     }
70 }
71 int main(void)
72 {
73     int t,m;
74     int a,b,d;
75     scanf("%d",&t);
76     while(t--)
77     {
78         init();
79         scanf("%d%d",&n,&m);
80         for(int i=1;i<n;i++){
81             scanf("%d%d%d",&a,&b,&d);
82             child[a].push_back(node(b,d));
83             child[b].push_back(node(a,d)); 
84         }
85         for(int i=1;i<=m;i++){
86             scanf("%d%d",&a,&b);
87             V[a].push_back(node(b,i));
88             V[b].push_back(node(a,i));
89             res[i][0]=a;
90             res[i][1]=b;
91         }
92         LCA(1);
93         for(int i=1;i<=m;i++){
94             printf("%d\n",dis[res[i][0]]+dis[res[i][1]]-2*dis[res[i][2]]);
95         }
96     }
97     return 0;
98 }
View Code

 

 再给出基于RMQ在线算法的做法:

 1 //78MS    13100K    1892 B    G++
 2 #include<stdio.h>
 3 #include<string.h>
 4 #include<math.h>
 5 #define N 40005
 6 struct node{
 7     int u,v,d;
 8     int next;
 9 }edge[2*N]; //邻接表 
10 int tot,head[N]; //记录先序遍历出现次序,邻接表头 
11 int set[2*N],first[N]; //记录父节点与次序数 
12 int dep[2*N],dis[N];  //记录深度和距离 
13 int dp[2*N][30];
14 bool vis[N];
15 void addedge(int u,int v,int d,int &k) //加边 
16 {
17     edge[k].u=u;
18     edge[k].v=v;
19     edge[k].d=d;
20     edge[k].next=head[u];
21     head[u]=k++;
22 }
23 int Min(int a,int b) //比较的是深度 
24 {
25     return dep[a]<dep[b]?a:b;
26 }
27 void dfs(int u,int deep) //dfs将LCA向RMQ转换 
28 {
29      vis[u]=true;
30      set[++tot]=u;
31      first[u]=tot;
32      dep[tot]=deep;
33      for(int i=head[u];i!=-1;i=edge[i].next){
34          if(vis[edge[i].v]) continue;
35          int v=edge[i].v,d=edge[i].d;
36          dis[v]=dis[u]+d;
37          dfs(v,deep+1);
38          set[++tot]=u;
39          dep[tot]=deep;
40      }
41 }
42 int init(int len)
43 {
44     int m=(int)(log(len*1.0)/log(2.0));
45     for(int i=1;i<=len;i++)
46         dp[i][0]=i;
47     for(int j=1;j<=m;j++)
48         for(int i=1;i+(1<<j)-1<=len;i++)
49             dp[i][j]=Min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
50 } 
51 int RMQ(int l,int r)
52 {
53     int m=(int)(log((r-l+1)*1.0)/log(2.0));
54     return Min(dp[l][m],dp[r-(1<<m)+1][m]);
55 }
56 int LCA(int u,int v)
57 {
58     int x=first[u];
59     int y=first[v];
60     if(x>y){
61         int temp=x;x=y;y=temp;
62     }
63     return set[RMQ(x,y)];
64 }
65 int main(void)
66 {
67     int t,n,q;
68     scanf("%d",&t);
69     while(t--)
70     {
71         int u,v,d;
72         int num=0;
73         scanf("%d%d",&n,&q);
74         memset(head,-1,sizeof(head));
75         memset(vis,false,sizeof(vis));
76         for(int i=1;i<n;i++){
77             scanf("%d%d%d",&u,&v,&d);
78             addedge(u,v,d,num);
79             addedge(v,u,d,num);
80         }
81         tot=0;
82         dis[1]=0;
83         dfs(1,1);
84         init(2*n-1);
85         while(q--){
86             scanf("%d%d",&u,&v);
87             printf("%d\n",dis[u]+dis[v]-2*dis[LCA(u,v)]);
88         }
89         
90     }
91     return 0;
92 }
View Code

 

 

posted @ 2014-04-13 11:12  heaventouch  阅读(244)  评论(0编辑  收藏  举报