【CodeVS1036】商务旅行

【Description】

  某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间。
  假设有N个城镇,首都编号为1,商人从首都出发,其他各城镇之间都有道路连接,任意两个城镇之间如果有直连道路,在他们之间行驶需要花费单位时间。该国公路网络发达,从首都出发能到达任意一个城镇,并且公路网络不会存在环。
  你的任务是帮助该商人计算一下他的最短旅行时间。


【Input】

  输入文件中的第一行有一个整数 N 1<=N<=30000,为城镇的数目。下面 N1 行,每行由两个整数 a b(1a,bN;ab) 组成,表示城镇 a 和城镇b有公路连接。在第 N+1 行为一个整数 M ,下面的M行,每行有该商人需要顺次经过的各城镇编号。


【Output】

  在输出文件中输出该商人旅行的最短时间。


【Sample Input】

5
1 2
1 5
3 5
4 5
4
1
3
2
5

【Sample Output】

7

【题解】

  题意很清楚,求起点至终点每两个点的最短路径之和,而这个图退化成了一棵树,我们可以想到求出两点的 LCA 再进行根据深度计算。具体来说,在以T为根的树中,对于两点 u v LCA(T,u,v)=p ,则 dist(u,v)=dep(u)+dep(v)2dep(p)
  
  参考代码如下:

type struct = record
       go,next:longint;
     end;
var n,m,ans,cnt,cntr:longint;
    a:array[0..60200] of struct; //存储边
    en,enr,father,depth:array[0..30100] of longint;
    visited:array[0..30100] of boolean;  //存储访问记录
    road:array[0..30100] of struct;  //存储询问
procedure addedge(u,v:longint);  //增加一条边
  begin
    inc(cnt); a[cnt].go:=v; a[cnt].next:=en[u]; en[u]:=cnt;
  end;
procedure addroad(u,v:longint);  //增加一个询问
  begin
    inc(cntr); road[cntr].go:=v; road[cntr].next:=enr[u]; enr[u]:=cntr;
  end;
procedure init;  //读入边
  var i,x,y:longint;
  begin
    readln(n);
    fillchar(a,sizeof(a),0);
    for i:=1 to n-1 do begin
      readln(x,y); addedge(x,y); addedge(y,x);
    end;
  end;
function getfather(x:longint):longint;
  begin
    if father[x]<>x then father[x]:=getfather(father[x]);
    exit(father[x]);
  end;
procedure tarjan(x,fa:longint);  //朴素Tarjan算法
  var i,tmp,t1,t2,t3:longint;
  begin
    father[x]:=x;
    i:=en[x];
    while (i<>0) do begin
      tmp:=a[i].go;
      i:=a[i].next;
      if tmp=fa then continue;
      tarjan(tmp,x);
      father[tmp]:=x;
    end;
    visited[x]:=true;
    i:=enr[x];
    while (i<>0) do begin
      tmp:=road[i].go;
      i:=road[i].next;
      if visited[tmp] then begin
        t1:=getfather(tmp);
        inc(ans,depth[x]+depth[tmp]-2*depth[t1]);
      end;
    end;
  end;
procedure calcdep(x,fa:longint);  //计算每个点深度(单独计算可提高速度)
  var i,tmp:longint;
  begin
    i:=en[x];
    while (i<>0) do begin
      tmp:=a[i].go;
      i:=a[i].next;
      if tmp=fa then continue;
      depth[tmp]:=depth[x]+1;
      calcdep(tmp,x);
    end;
  end;
procedure main;  //读取询问并计算
  var i,t,k:longint;
  begin
    readln(m); readln(t);
    for i:=2 to m do begin
      readln(k);
      addroad(t,k); addroad(k,t);
      t:=k;
    end;
    fillchar(visited,sizeof(visited),false);
    depth[1]:=1;
    calcdep(1,1);
    tarjan(1,1);
    writeln(ans);
  end;
begin
  init;
  main;
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值