<最小生成树><lca>Heatwave

题目

给你N个点的无向连通图,图中有M条边,第j条边的长度为: dj 现在有 K个询问。每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

数据范围

50% 1<=N,M<=3000
其中30% K<=5000   
100% 1 <= N <= 15,000 1 <= M <= 30,000 1 <= dj<= 1,000,000,000   1 <= K <= 20,000

分析

从题目看,有一句话很重要:表示询问从A点走到B点的所有路径中,最长的边最小值是多少?”所有“这词提醒了我们这些路径中有很多是无用的,是浪费的。我们会发现这题要求的条件也很特殊,也就是只要是连通的路径,就可以取里面的最大值了。再者,既然最大值最小,那么我们可以先使这些选出来的路径尽量的小,且可以使他们连通就可以了,这就变成了一棵树,那么也就是说这n个点中我们可以先取n-1条边,使得这n-1条边可以代表这些其他边,还有要最小,自然而然就是最小生成树了!做完最小生成树后这个图就变成一棵树,然后再处理这棵树每两个点之间的最大边的值,也就是用 lca+rmq 处理,便可以使复杂度变成 Nlogn 的。

补充kruskal

即贪心地选n-1条边,使得可以让全部点连通。就是用并查集来判断连通,快排边权,再连。这里写图片描述
简单来说就是:

   qsort(1,n); 
   for i=1 to n do
   if b[i][1]与b[i][2]不连通{
      连双向边b[i][1]-b[i][2];
      合并b[i][1]和b[i][2]的子集;
   }

补充最长公共祖先

首先要求一次dfs序,使得整棵树有编号。再 RMQ 去处理每个点往上的 2j 个点的编号,这时我们就可以先把 x,y 调成一个深度即

    lca:=0;
    if d[x]<d[y] then begin k:=x;x:=y;y:=k;end;
    k:=trunc(ln(d[x]-d[y]+1)/ln(2));
    while k>=0 do begin
        if d[g[x,k]]>d[y] then begin
            lca:=max(lca,wer[x,k]);x:=g[x,k];
        end;
        dec(k);
    end;

然后就开始找到一个公共的祖先就可以了:

if d[x]<>d[y] then begin lca:=max(lca,wer[x,0]);x:=g[x,0];end;
    k:=trunc(ln(d[x])/ln(2));
    while k>=0 do begin
        if g[x,k]<>g[y,k] then begin
           lca:=max(max(lca,wer[x,k]),wer[y,k]);x:=g[x,k];y:=g[y,k];
        end;
        dec(k);
    end;
    if x=y then exit(lca) else exit(max(lca,max(wer[x,0],wer[y,0])));
end;

其实就是一直往上跳的一个过程。

注意

在求两点之间的最小边权时,可以在求最长公共祖先时同时求。

var
    a:array[0..30000,1..3] of longint;
    i,n,m,k,gx,gy,op,x,y,num,j:longint;
    fa,d:array[0..15000] of longint;
    b,next,last,f:array[1..80000] of longint;
    g,wer:array[0..15000,0..16] of longint;
function max(l,r:longint):longint;
begin
    if l<r then exit(r) else exit(l);
end;
procedure qsort(l,r:longint);
var i,j,mid:longint;
begin
    i:=l;j:=r;mid:=a[(l+r)shr 1,3];
    repeat
       while a[i,3]<mid do inc(i);
       while a[j,3]>mid do dec(j);
       if i<=j then begin
          a[0]:=a[i];a[i]:=a[j];a[j]:=a[0];inc(i);dec(j);
       end;
    until i>j;
    if l<j then qsort(l,j);
    if i<r then qsort(i,r);
end;
function get(k:longint):longint;
begin
    if fa[k]=0 then exit(k)
    else begin
        fa[k]:=get(fa[k]);
        exit(fa[k]);
    end;
end;
procedure insert(x,y,z:longint);
begin
    inc(num);
    b[num]:=y;
    next[num]:=last[x];
    last[x]:=num;
    f[num]:=z;
end;
procedure dfs(k,y:longint);
var p:longint;
begin
    p:=last[k];
    while p<>0 do begin
       if b[p]<>y then begin
       g[b[p],0]:=k;
       d[b[p]]:=d[k]+1;
       wer[b[p],0]:=f[p];
       dfs(b[p],k);
       end;
       p:=next[p];
    end;
end;
function lca(x,y:longint):longint;
var k:longint;
begin
    lca:=0;
    if d[x]<d[y] then begin k:=x;x:=y;y:=k;end;
    k:=trunc(ln(d[x]-d[y]+1)/ln(2));
    while k>=0 do begin
        if d[g[x,k]]>d[y] then begin
            lca:=max(lca,wer[x,k]);x:=g[x,k];
        end;
        dec(k);
    end;
    if d[x]<>d[y] then begin lca:=max(lca,wer[x,0]);x:=g[x,0];end;
    k:=trunc(ln(d[x])/ln(2));
    while k>=0 do begin
        if g[x,k]<>g[y,k] then begin
           lca:=max(max(lca,wer[x,k]),wer[y,k]);x:=g[x,k];y:=g[y,k];
        end;
        dec(k);
    end;
    if x=y then exit(lca) else exit(max(lca,max(wer[x,0],wer[y,0])));
end;
begin
    readln(n,m,k);
    for i:=1 to m do readln(a[i,1],a[i,2],a[i,3]);
    qsort(1,m);
    for i:=1 to m do
    if get(a[i,1])<>get(a[i,2]) then begin
       insert(a[i,1],a[i,2],a[i,3]);
       insert(a[i,2],a[i,1],a[i,3]);
       gx:=get(a[i,1]);
       gy:=get(a[i,2]);
       fa[gx]:=gy;
    end;
    d[1]:=1;
    dfs(1,0);
    for j:=1 to trunc(ln(n)/ln(2)) do
        for i:=1 to n do begin
           g[i,j]:=g[g[i,j-1],j-1];
           wer[i,j]:=max(wer[i,j-1],wer[g[i,j-1],j-1]);
        end;
    for i:=1 to k do begin
        readln(x,y);
        writeln(lca(x,y));
    end;
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值