jzoj3395 Freda的传呼机

19 篇文章 0 订阅

Description

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

解题报告

A类直接LCA
B类考虑两种情况: 去掉一条边成树的情况,然后再加上那条边,求到那条边两个端点的距离再加上那条边的长度,取 MIN
C类:传说中的业界毒瘤仙人掌图。
可以先去环。
考虑一个这样的图:
这里写图片描述
要求5到3之间最短路径。
可以为每个环规定一个环顶,然后将环内其他顶点向环顶连边。这条边的权值就是该点到环顶的最小距离。
这个可以一边dfs求出环的总长度,然后将环长度减去一条路径的长度就是另一条路径的长度了。
这里写图片描述
因为在同一个环之前的最小路径唯一,所以直接lca就可以了。
先把5上升到与3的lca下的第一层,然后看他们是否在同一环内。如果是的话,就求环内最小距离+lca,如果不是的话就直接原lca长度+两条连往lca的边即可即可。

环内最小距离如何求?
记录一下两点走两条路径到环顶的距离的距离。然后就可以愉快的分类讨论:经过环顶,不经过环顶了。

CODE

var
    hh,hu,pr,su,tw,w,tnext,next,e,te:array[-60000..60000] of longint;
    len,dst,s,deep,father,thead,head:array[0..10000] of longint;
    vis:array[1..10000] of boolean;
    f,g:array[0..10000,0..14] of longint;
    rt,i,ru,l,r,k,ttot,tot,n,m,q,j,x,y,t,ht:longint;
procedure new(a,b,t:longint);
begin
    inc(tot);
    e[tot]:=b;  w[tot]:=t;
    next[tot]:=head[a];
    head[a]:=tot;

    e[tot+m]:=a;  w[tot+m]:=t;
    next[tot+m]:=head[b];
    head[b]:=tot+m;
end;
procedure tnew(a,b,t:longint);
begin
    inc(ttot);
    te[ttot]:=b;  tw[ttot]:=t;
    tnext[ttot]:=thead[a];
    thead[a]:=ttot;

    te[ttot+m]:=a;  tw[ttot+m]:=t;
    tnext[ttot+m]:=thead[b];
    thead[b]:=ttot+m;
end;
function min(a,b:longint):longint;
begin
    if a>b then exit(b) else exit(a);
end;
procedure swap(var a,b:longint);
var t:longint;
begin
    t:=a; a:=b; b:=t;
end;

function lca(x,y:longint):longint;
var i:longint;
begin
    lca:=0;
    if deep[x]<deep[y] then swap(x,y);
    for i:=14 downto 0 do
        if deep[g[x,i]]>deep[y] then
        begin
            inc(lca,f[x,i]);
            x:=g[x,i];
        end;
    if deep[x]<>deep[y] then
    begin
        inc(lca,f[x,0]);
        x:=g[x,0];
    end;
    if x=y then exit(lca);

    for i:=14 downto 0 do
        if g[x,i]<>g[y,i] then
        begin
            inc(lca,f[x,i]+f[y,i]);
            x:=g[x,i]; y:=g[y,i];
        end;
    if (hh[x]=hh[y])and(hh[x]+hh[y]<>0)and(m>n-1) then
    begin
        exit(lca+min(abs(pr[x]-pr[y]),min(pr[x]+su[y],pr[y]+su[x])));
    end else exit(lca+f[x,0]+f[y,0]);
end;

procedure init_lca(x:longint);
begin
    for i:=1 to 14 do
    begin
        g[x,i]:=g[g[x,i-1],i-1];
        f[x,i]:=f[g[x,i-1],i-1]+f[x,i-1];
    end;
end;

procedure circle(x,fa,way,lastpath:longint);
var i,k:longint;
begin
    inc(s[0]); s[s[0]]:=x;
    dst[x]:=way;
    vis[x]:=true;
    k:=head[x];
    while (k<>0) do
    begin
        if vis[e[k]]=false then
            circle(e[k],x,way+w[k],k)
        else
            if (hu[k]=0)and(k<>lastpath+m)and(k<>lastpath-m) then
            begin
                hu[k]:=1; hu[k-m]:=1; hu[k+m]:=1;
                i:=s[0];
                len[e[k]]:=dst[x]-dst[e[k]]+w[k];
                inc(ht);
                while s[i]<>e[k] do
                begin
                    hh[s[i]]:=ht;
                    father[s[i]]:=e[k];
                    pr[s[i]]:=dst[s[i]]-dst[e[k]];
                    su[s[i]]:=len[e[k]]-(dst[s[i]]-dst[e[k]]);

                    tnew(e[k],s[i],min(dst[s[i]]-dst[e[k]],
                                       len[e[k]]-(dst[s[i]]-dst[e[k]])));
                    dec(i);
                end;
            end;
        k:=next[k];
    end;
    if (hh[x]=0)and(fa<>0) then
    begin
        tnew(fa,x,w[lastpath]);
    end;

    dec(s[0]);
end;
procedure dfs(x,way,dep,fa:longint);
var
    k:longint;
begin
    dst[x]:=way;
    deep[x]:=dep;
    init_lca(x);
    k:=thead[x];
    while k<>0 do
    begin
        if te[k]<>fa then
        begin
            f[te[k],0]:=tw[k];
            g[te[k],0]:=x;
            dfs(te[k],way+tw[k],dep+1,x);
        end;
        k:=tnext[k];
    end;
end;

procedure init;
begin
    deep[0]:=-maxlongint;
    readln(n,m,q);
    for i:=1 to m do
    begin
        readln(x,y,t);
        new(x,y,t);
    end;
    if m>n-1 then circle(1,0,0,0) else
    begin
        tw:=w;
        thead:=head;
        tnext:=next;
        te:=e;
        ttot:=tot;
    end;
    dfs(1,0,1,0);
end;
begin
    init();
    for i:=1 to q do
    begin
        readln(x,y);
        writeln(lca(x,y));
    end;
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值