【RMQ&LCA】Distance Queries(距…

【RMQ&LCA】Distance Queries(距离查询)

Time Limit:1000MS  Memory Limit:65536K
Total Submit:24 Accepted:8

Description

距离查询(qu.pas/c/cpp)

【问题描述】

    农民约翰的奶牛拒绝在跑他的马拉松,因为他选择的路径太长不适合奶牛们悠闲的生活方式。因此,他希望找到一个更合理的路径长度。农民约翰要查询一系列点对间的路径长度,请尽快回答农民约翰的询问!

Input

第一行包括两个整数N,M,N (2 <= N <= 40,000)表示农场数量,每个农场标记为1-N,M (1 <= M < 40,000)表示路径数量。
第二至M+1行,每行包括三个整数F1,F2,L和一个字符D,表示农场F1和F2之间的路径长度为L,D为'N', 'E', 'S', 'W'中的一个,表示从F1到F2的方向。
第M+2行包括一个整数K,1 <= K <= 10,000
第M+3至M+K+2行,每行包括两个整数,表示要询问的两个农场。

Output

共K行,每行输出每个询问的路径长度。

Sample Input

7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S
3
1 6
1 4
2 6



Sample Output

13
3
36

Hint

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

Source

USACO 2004 February

 

本题的图为一棵树。

先随便设一个根,算出每个点i到根的距离的的d[i]。

对于每一个询问x,y,距离为 d[x]+d[y]-d[x,y点的最近公共祖先]×2

 

一开始纠结于树的构建。

因为最多有40000条无向边,储存时就要储存80000条有向边。

    最多有10000条询问,储存时就要储存20000条询问。

    用LCA计算时要很快的得知一个父节点的 所有儿子 和 所有询问

开始只想用最简单的二维储存(a[i,j]表示i是j的父亲),但空间不允许,纠结了一会。

然后突然想到用链表。。。。。。。自己真是蠢透了。。

 

type
 arrt=record
 x,y,d,next:longint;
 end;
 arrq=record
 x,y,next,q:longint;
 end;
var
 n,m,kk:longint;
 q:array[1..10000*2+1]of arrq;
 lastq:array[1..40000*2+1]of longint;
 tree:array[1..40000*2+1]of arrt;
 last:array[1..40000*2+1]of longint;
 d:array[1..40000*2+1]of longint;
 mark:array[1..40000*2+1]of boolean;
 f:array[1..40000*2+1]of longint;

procedure dfs(x:longint);
var
 i,y,k:longint;
begin
 y:=last[x];
 while y<>0 do
  begin
   k:=tree[y].y;
   if not mark[k] then
    begin
    d[k]:=d[x]+tree[y].d;
    mark[k]:=true;
    dfs(k);
    end;
   y:=tree[y].next;
  end;
end;

function getfather(x:longint):longint;
begin
 if f[x]=x then exit(x)
 else
  begin
  f[x]:=getfather(f[x]);
  exit(f[x]);
  end;
end;

procedure lca(x,s:longint);
var
 i,y,k:longint;
begin
 f[x]:=x;
 y:=last[x];
 while y<>0 do
   begin
    k:=tree[y].y;
    if k<>s then
     begin
     lca(k,x);
     f[k]:=x;
     end;
    y:=tree[y].next;
   end;
 mark[x]:=true;
 y:=lastq[x];
 while y<>0 do
   begin
    k:=q[y].y;
    if mark[k] then q[y].q:=d[x]+d[k]-d[getfather(k)]*2;
    y:=q[y].next;
   end;
end;

procedure init;
var
 i,x,y,z:longint;
begin
 read(n,m);
 for i:=1 to m do
  begin
  readln(x,y,z);
  tree[i].x:=x;
  tree[i].y:=y;
  tree[i].d:=z;
  tree[i].next:=last[x];
  last[x]:=i;
  tree[n+i].x:=y;
  tree[n+i].y:=x;
  tree[n+i].d:=z;
  tree[n+i].next:=last[y];
  last[y]:=n+i;
  end;
 read(kk);
 for i:=1 to kk do
  begin
  readln(x,y);
  q[i].x:=x;
  q[i].y:=y;
  q[i].next:=lastq[x];
  lastq[x]:=i;

  q[i+kk].x:=y;
  q[i+kk].y:=x;
  q[i+kk].next:=lastq[y];
  lastq[y]:=i+kk;
  end;
end;

procedure print;
var
 i:longint;
begin
 for i:=1 to kk do
  if q[i].q<>0 then writeln(q[i].q)
               else writeln(q[i+kk].q);
end;

begin
 init;
 fillchar(d,sizeof(d),0);
 fillchar(mark,sizeof(mark),0);
 mark[1]:=true;
 dfs(1);
 fillchar(mark,sizeof(mark),0);
 lca(1,0);
 print;
end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值