【NOIP2013模拟】Freda的传呼机

【NOIP2013模拟】Freda的传呼机

Time Limits: 100 ms Memory Limits: 131072 KB

Description
为了 随时 与 rainbow快速交流, Freda制造了 两部传呼机 。Freda和 rainbow所在的地方有N座房屋、M条双向 光缆 。每条光缆连接两座房屋, 传呼机发出的信号只能沿着光缆传递,并且 传呼机的信号 从光缆的其中一端传递到另需要花费 t单位时间 。现在 Freda要 进行 Q次试验, 每次选取两座房屋,并想知道 传呼机的信号在这两座房屋之间传递 至少需 要多长时间。 Freda 和 rainbow简直弱爆了有木有T_TT_T ,请你帮他们吧……

N座房屋 通过光缆 一定是连通的, 并且这 M条光缆有以下三类连接情况:

A:光缆不形成环 ,也就是光缆仅 有 N-1条。
B:光缆只 形成一个环,也就是光缆 仅有 N条。
C:每条光缆仅在一个环中。

Input
第一行 包含三个用空格隔开的整数, N、M和 Q。
接下来 M行每三个整数 x、y、t,表示 房屋 x和 y之间有一条传递时为 t的光缆 。
最后 Q行每两个整数 x、y,表示 Freda想知道 在 x和 y之间传呼最少需要多长时间。

Output
输出 Q行,每一个整数表示 Freda每次试验的结果 。

Sample Input
Input1:
5 4 2
1 2 1
1 3 1
2 4 1
2 5 1
3 5
2 1

Input2:
5 5 2
1 2 1
2 1 1
1 3 1
2 4 1
2 5 1
3 5
2 1

Input3:
9 10 2
1 2 1
1 4 1
3 4 1
2 3 1
3 7 1
7 8 2
7 9 2
1 5 3
1 6 4
5 6 1
1 9
5 7

Sample Output
Output1:
3
1

Output2:
3
1

Output3:
5
6

Data Constraint

  • 送分数据占10%,2<=N<=1000,N-1<=M<=1200。
  • A类数据占30%,M=N-1。
  • B类数据占50%,M=N。
  • C类数据占10%,M>N。
  • 对于100%的数据,2<=N<=10000,N-1<=M<=12000,Q=10000,1<=x,y<=N,1<=t<=32768。

题目大意

给你一个图,图上每一条边最多只能在一个环中,给出Q组询问,每组询问是x,y,答x到y的最短距离


解题思路

业界毒瘤?仙人掌?不懂,总之讲一讲我的方法吧:
方法就是lca,可以将每个环看作一个节点,也就是说,除了环顶之外,其余环中的看作一点,那么我们就可以求出fa[x] (环顶或者父亲节点)和 di[x] (这个点到父亲节点的距离或到环顶的两条距离中,最小值)

  1. 我们可以首先用dfs找fa,di,标记光缆编号。
  2. 然后再一次dfs找出节点所在的层
  3. 最后lca一遍。

Codes

const
    maxn=19231; maxx=83721;
var dis,s,p,first,last,f,owner,floor:array[0..10000]of longint;
    fa,di:array[0..10000,0..16]of longint;
    hush:array[0..maxn,1..3]of longint;
    n,m,i,x,y,t,q,tot,j,top,z:longint;
    a:array[0..24000,1..4]of longint;
    bz,pd:array[0..12000]of boolean;
    stack:array[0..10000,1..2]of longint;
function ha(x,y:longint):longint; //哈希求每条光缆的编号
    var z:longint;
begin
    z:=((x*maxx)+(y*y*2-1))mod maxn;
    while (hush[z,3]<>0)and((hush[z,1]<>x)or(hush[z,2]<>y)) do z:=z mod maxn+1;
    exit(z);
end;
procedure insert(w,x,y,z:longint); //插入边集数组
begin
    a[w,1]:=x; a[w,2]:=y; a[w,3]:=z; a[w,4]:=w;
    if first[x]=0 then first[x]:=w else a[last[x],4]:=w;
    last[x]:=w;
end;
function turn(x:longint):longint;
begin
    if x>m then x:=x-m;
    exit(x);
end;
function min(p,q:longint):longint;
begin
    if p<q then exit(p) else exit(q);
end;
procedure build(x,v,n,last:longint);  //找环,做fa,di
    var o,y,t:longint;
begin
    if f[x]<>0 then   //找到一个环
    begin
        inc(tot);
        dis[tot]:=v-stack[f[x],2];
        for o:=f[x]+1 to n-1 do
        begin
            y:=stack[o,1];
            pd[y]:=true;
            s[y]:=stack[o,2]-stack[f[x],2];
            p[y]:=dis[tot]-s[y];
            fa[y,0]:=x; di[y,0]:=min(s[y],p[y]);
            owner[y]:=tot;  //一个点的归属环
        end;
    end else
    begin
        f[x]:=n;  //将每个节点存入栈中
        stack[n,1]:=x;
        stack[n,2]:=v;
        o:=first[x];
        while 1=1 do
        begin
            t:=turn(o);
            if bz[t] then  //判断该边是否走过
            begin
                if o=a[o,4] then break;
                o:=a[o,4];
                continue;
            end;
            y:=a[o,2];
            bz[t]:=true;
            build(y,v+a[o,3],n+1,x);
            if a[o,4]=o then break;
            o:=a[o,4];
        end;
        if not pd[x] then  //处理不是环的情况
        begin
            inc(tot);
            owner[x]:=tot;
            fa[x,0]:=last;
            di[x,0]:=stack[n,2]-stack[n-1,2];
        end;
    end;
end;
procedure find(x,t:longint);  //求每个点的层数
    var o,y:longint;
begin
    o:=first[x];
    floor[x]:=t;
    bz[x]:=true;
    while 1=1 do
    begin
        y:=a[o,2];
        if bz[y] then
        begin
            if a[o,4]=o then break;
            o:=a[o,4];
            continue;
        end;
        if owner[y]<>owner[x] then find(y,t+1) else find(y,t);
        if a[o,4]=o then break;
        o:=a[o,4];
    end;
end;
function min(x,y,z:longint):longint;
begin
    if x>y then x:=y;
    if x>z then x:=z;
    exit(x);
end;
procedure swap(var a,b:longint);
    var c:longint;
begin
    c:=a; a:=b; b:=c;
end;
function ask(x,y:longint):longint;  //求两点的最短路径
    var dis,o:longint;
begin
    if floor[x]>floor[y] then swap(x,y);
    dis:=0;
    for o:=16 downto 0 do
        if 1<<o<=floor[y]-floor[x] then
        begin
            inc(dis,di[y,o]);
            y:=fa[y,o];
        end;
    for o:=16 downto 0 do
    begin
        if fa[x,o]<>fa[y,o] then
        begin
            inc(dis,di[x,o]+di[y,o]);
            x:=fa[x,o]; y:=fa[y,o];
        end;
    end;
    if owner[x]=owner[y] then exit(dis+min(abs(s[x]-s[y]),s[x]+p[y],p[x]+s[y])) else exit(dis+di[x,0]+di[y,0]);
end;
begin
    assign(input,'pager.in'); reset(input);
    assign(output,'pager.out'); rewrite(output);
    read(n,m,q);
    for i:=1 to m do
    begin
        read(x,y,z);
        if x>y then swap(x,y);
        t:=ha(x,y);
        if hush[t,3]<>0 then  //将两点之间的所有直接到达的路求最小值
        begin
            t:=hush[t,3];
            a[t,3]:=min(a[t,3],z);
            a[t+m,3]:=min(a[t+m,3],z);
        end else
        begin
            inc(top);
            hush[t,1]:=x; hush[t,2]:=y; hush[t,3]:=top;
            insert(top,x,y,z);
            insert(top+m,y,x,z);
        end;
    end;
    build(1,0,1,0);
    for j:=1 to 16 do  //搞lca
        for i:=1 to n do
        begin
            fa[i,j]:=fa[fa[i,j-1],j-1];
            di[i,j]:=di[i,j-1]+di[fa[i,j-1],j-1];
        end;
    fillchar(bz,sizeof(bz),false);
    find(1,1);
    for i:=1 to q do
    begin
        read(x,y);
        writeln(ask(x,y));
    end;
    close(output); close(input);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值