POJ 1986 Distance Queries (Tarjan-LCA算法)(带权值)

来源:互联网 发布:!号c语言中是什么意思 编辑:程序博客网 时间:2024/06/05 14:36
Distance Queries
Time Limit: 2000MS Memory Limit: 30000KTotal Submissions: 14854 Accepted: 5229Case Time Limit: 1000MS

Description

Farmer John's cows refused to run in his marathon since he chose a path much too long for their leisurely lifestyle. He therefore wants to find a path of a more reasonable length. The input to this problem consists of the same input as in "Navigation Nightmare",followed by a line containing a single integer K, followed by K "distance queries". Each distance query is a line of input containing two integers, giving the numbers of two farms between which FJ is interested in computing distance (measured in the length of the roads along the path between the two farms). Please answer FJ's distance queries as quickly as possible! 

Input

* Lines 1..1+M: Same format as "Navigation Nightmare" 

* Line 2+M: A single integer, K. 1 <= K <= 10,000 

* Lines 3+M..2+M+K: Each line corresponds to a distance query and contains the indices of two farms. 

Output

* Lines 1..K: For each distance query, output on a single line an integer giving the appropriate distance. 

Sample Input

7 61 6 13 E6 3 9 E3 5 7 S4 1 3 N2 4 20 W4 7 2 S31 61 42 6

Sample Output

13336

Hint

Farms 2 and 6 are 20+3+13=36 apart. 

题目大意:John是一个农场主,他的牛很懒,拒绝按照John选的路走。John不得不找一条

最短的路。

思路:本题输入有些特殊,给出的是某点在某点的某个方向(东西南北)有多远。由于输入数

据比较特殊。可以根据直接套用Tarjan-LCA算法的模板,考虑树上每对节点的最短路径计算。如果确定根为1,则有

Dist(u,v) = Dist(1,u) + Dist(1,v) - 2*Dist( 1,LCA(u,v) )。先深搜一次遍历求出每个

节点到根结点的距离,再做一次LCA,就可以得出结果了。


代码:

#include<stdio.h>#include<iostream> #include <algorithm>#include<string.h>#include<vector>#include<math.h>#include<queue>#include<deque>//双端队列 #include<set>#define MOD 1e9+7#define LL long long#define INF 0x3f3f3f3fusing namespace std;int KGCD(int a,int b){if(a==0)return b;if(b==0)return a;if(~a&1){ if(b&1) return KGCD(a>>1,b);else return KGCD(a>>1,b>>1) <<1; } if(~b & 1)  return KGCD(a, b>>1);  if(a > b) return KGCD((a-b)>>1, b);return KGCD((b-a)>>1, a);}  int LCM(int a,int b){ return a/KGCD(a,b)*b; } int dir[5][2]={0,1,0,-1,1,0,-1,0};using namespace std;struct edge{int u; //记录起点 int next;//记录同起点的下一条边 int v;//终点边 int w;//权值 }e[400009],e1[400009];int head[400009]; int head1[400009];int fa[400009];int dis[400009];//记录到根节点距离int vis[400009];//标记int tot,tot1;void init(){memset(dis,0,sizeof(dis)); memset(head1,-1,sizeof(head1));memset(head,-1,sizeof(head));tot=0;tot1=0; }void add(int u,int v,int cost)//老树 {e[tot].u=u;e[tot].v=v;e[tot].w=cost;e[tot].next=head[u];head[u]=tot;tot++; } void add1(int u,int v)//新的小树 {e1[tot1].u=u;e1[tot1].v=v;e1[tot1].next=head1[u];head1[u]=tot1;tot1++;}int find(int root)//路径压缩  {if(root!=fa[root])fa[root]=find(fa[root]);return fa[root];}void LCA(int root){fa[root]=root;//本人默认是祖先 方便查找 vis[root]=1;for(int i=head[root];i!=-1;i=e[i].next){if(vis[e[i].v]==0){dis[e[i].v]=dis[root]+e[i].w;LCA(e[i].v);fa[e[i].v]=root;//更新父亲节点 }} for(int k=head1[root];k!=-1;k=e1[k].next){ if(vis[e1[k].v])//当前需要的点被访问过了 { e1[k].w=dis[root]+dis[e1[k].v]-2*dis[find(e1[k].v)];e1[k^1].w=e1[k].w;//反向边 }} } int main(){int m,n,cost,x,y;char c;init();scanf("%d%d",&x,&y);for(int i=0;i<y;i++){scanf("%d%d%d %c",&m,&n,&cost,&c);add(m,n,cost);add(n,m,cost);}scanf("%d",&m);while(m--){scanf("%d%d",&x,&y);add1(x,y);add1(y,x); }LCA(1);for(int i=0;i<tot1;i+=2)//双向 所以加2 {printf("%d\n",e1[i].w);}return 0;}