JZOJsenior1738.Heatwave

该博客主要介绍了如何利用最小生成树(MST)解决图论问题,针对‘Heatwave’题目,通过Kruskal算法求MST并计算每个点的深度。接着,博主探讨了两种方法来查询两点间最长边的最小值:一是采用树上倍增算法,维护每个节点的祖先和路径最小值;二是应用树链剖分,利用线段树维护重链的最大值。文章提供了两种方法的代码实现,并分析了它们的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

problem

Description

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

Input

  文件名为heatwave.in
  第一行: N, M, K。
  第2..M+1行: 三个正整数:X, Y, and D (1 <= X <=N; 1 <= Y <= N). 表示X与Y之间有一条长度为D的边。
  第M+2..M+K+1行: 每行两个整数A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

Output

  对每个询问,输出最长的边最小值是多少。

Sample Input

6 6 8
1 2 5
2 3 4
3 4 3
1 4 8
2 5 7
4 6 2
1 2
1 3
1 4
2 3
2 4
5 1
6 2
6 1

Sample Output

5
5
5
4
4
7
4
5

Data Constraint

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

Hint


analysis

既然题目要求两点间最长边的最小值,那么我们就把这幅图的MST先求出来
这样两点间一定联通且满足两点间最长边最小(没有为什么自己想一想因为这点比较明显)
MST用kruskal求,比较基础,我就当你会了
求完MST之后把整棵树遍历一次,求出每个点的深度

树上倍增

(这题的JZ数据buff加成导致O(n)LCA乱搞也能AC……然后嘿嘿嘿
然后打了个奇丑无比倍增
维护两个数组anc[i,j]dis[i,j]分别表示i节点的2^j号祖先和i节点的与第2^j号祖先之间的路径最小值
接下来就和普通倍增没什么区别了,先把两个点调至同一深度,然后一起往上跳,跳的过程中记录答案

时间复杂度O(mlog2m+nlog2n+qlog2n)


树链剖分

树剖当然也能做啦!
把树剖分完以后,用线段树维护整一条重链的max
树剖LCA过程中查询线段树记录最大值
对于树剖来说这就是一道模板题

时间复杂度O(n+qlog22n)


code

树上倍增code(50+ms)

var
        a,edge:array[0..15000]of array of longint;
        anc,dis:array[0..15000,0..15]of longint;
        b:array[0..30000,0..3]of longint;
        depth,father:array[0..15000]of longint;
        n,m,q,i,j,x,y:longint;

function max(x,y:longint):longint;
begin
        if x>y then exit(x);
        exit(y);
end;

procedure swap(var x,y:longint);
var
        z:longint;
begin
        z:=x;
        x:=y;
        y:=z;
end;

procedure qsort(l,r:longint);
var
        i,j,mid:longint;
begin
        i:=l;
        j:=r;
        mid:=b[(l+r)div 2,3];
        repeat
                while b[i,3]<mid do inc(i);
                while b[j,3]>mid do dec(j);
                if i<=j then
                begin
                        b[0]:=b[i];
                        b[i]:=b[j];
                        b[j]:=b[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 getfather(x:longint):longint;
begin
        if father[x]=x then exit(x);
        father[x]:=getfather(father[x]);
        exit(father[x]);
end;

procedure dfs(x,y:longint);
var
        i:longint;
begin
        for i:=1 to a[x,0] do
        if a[x,i]<>y then
        begin
                anc[a[x,i],0]:=x;
                depth[a[x,i]]:=depth[x]+1;
                dis[a[x,i],0]:=edge[x,i];
                dfs(a[x,i],x);
        end;
end;

function lca(x,y:longint):longint;
var
        k:longint;
begin
        lca:=0;
        if depth[x]<depth[y] then swap(x,y);
        k:=trunc(ln(depth[x]-depth[y]+1)/ln(2));
        while k>=0 do
        begin
                if depth[anc[x,k]]>depth[y] then
                begin
                        lca:=max(lca,dis[x,k]);
                        x:=anc[x,k];
                end;
                dec(k);
        end;
        if depth[x]<>depth[y] then
        begin
                lca:=max(lca,dis[x,0]);
                x:=anc[x,0];
        end;
        k:=trunc(ln(depth[x])/ln(2));
        while k>=0 do
        begin
                if anc[x,k]<>anc[y,k] then
                begin
                        lca:=max(max(lca,dis[x,k]),dis[y,k]);
                        x:=anc[x,k];
                        y:=anc[y,k];
                end;
                dec(k);
        end;
        if x=y then exit(lca);
        exit(max(lca,max(dis[x,0],dis[y,0])));
end;

begin                                               
        readln(n,m,q);
        for i:=1 to n do
        begin
                father[i]:=i;
                setlength(a[i],1);
                a[i,0]:=0;
                setlength(edge[i],1);
                edge[i,0]:=0;
        end;
        for i:=1 to m do
        begin
                for j:=1 to 3 do read(b[i,j]);
                readln;
        end;
        qsort(1,m);
        for i:=1 to m do
        begin
                x:=b[i,1];
                y:=b[i,2];
                if getfather(x)<>getfather(y)then
                begin
                        father[getfather(x)]:=getfather(y);
                        inc(a[x,0]);
                        setlength(a[x],a[x,0]+1);
                        a[x,a[x,0]]:=y;
                        inc(edge[x,0]);
                        setlength(edge[x],edge[x,0]+1);
                        edge[x,a[x,0]]:=b[i,3];
                        inc(a[y,0]);
                        setlength(a[y],a[y,0]+1);
                        a[y,a[y,0]]:=x;
                        inc(edge[y,0]);
                        setlength(edge[y],edge[y,0]+1);
                        edge[y,a[y,0]]:=b[i,3];
                end;
        end;
        depth[1]:=1;
        dfs(1,0);
        for j:=1 to trunc(ln(n)/ln(2)) do
        for i:=1 to n do
        begin
                anc[i,j]:=anc[anc[i,j-1],j-1];
                dis[i,j]:=max(dis[i,j-1],dis[anc[i,j-1],j-1]);
        end;
        for i:=1 to q do
        begin
                readln(x,y);
                writeln(lca(x,y));
        end;
end.
var
        b,c,next,last,father,size:array[0..80000]of longint;
        anc,dis:array[0..15000,0..16]of longint;
        a:array[0..30000,1..3] of longint;
        n,m,i,j,k,x,y,tot:longint;

procedure swap(var x,y:longint);
var
        z:longint;
begin
        z:=x;
        x:=y;
        y:=z;
end;

function max(x,y:longint):longint;
begin
        if x>y then exit(x);
        exit(y);
end;

procedure qsort(l,r:longint);
var
        i,j,mid:longint;
begin
        i:=l;
        j:=r;
        mid:=a[(l+r)div 2,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 getfather(k:longint):longint;
begin
        if father[k]=0 then exit(k);
        father[k]:=getfather(father[k]);
        exit(father[k]);
end;

function judge(x,y:longint):boolean;
begin
        exit(getfather(x)=getfather(y));
end;

procedure insert(x,y,z:longint);
begin
        inc(tot);
        b[tot]:=y;
        next[tot]:=last[x];
        last[x]:=tot;
        c[tot]:=z;
end;

procedure dfs(x,y:longint);
var
        now:longint;
begin
        now:=last[x];
        while now<>0 do
        begin
                if b[now]<>y then
                begin
                        anc[b[now],0]:=x;
                        size[b[now]]:=size[x]+1;
                        dis[b[now],0]:=c[now];
                        dfs(b[now],x);
                end;
                now:=next[now];
        end;
end;

function lca(x,y:longint):longint;
var
        k:longint;
begin
        lca:=0;
        if size[x]<size[y] then swap(x,y);
        k:=trunc(ln(size[x]-size[y]+1)/ln(2));
        while k>=0 do
        begin
                if size[anc[x,k]]>size[y] then
                begin
                        lca:=max(lca,dis[x,k]);
                        x:=anc[x,k];
                end;
                dec(k);
        end;
        if size[x]<>size[y] then
        begin
                lca:=max(lca,dis[x,0]);
                x:=anc[x,0];
        end;
        k:=trunc(ln(size[x])/ln(2));
        while k>=0 do
        begin
                if anc[x,k]<>anc[y,k] then
                begin
                        lca:=max(max(lca,dis[x,k]),dis[y,k]);
                        x:=anc[x,k];
                        y:=anc[y,k];
                end;
                dec(k);
        end;
        if x=y then exit(lca);
        exit(max(lca,max(dis[x,0],dis[y,0])));
end;

begin
        assign(input,'readin.pas');reset(input);
        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
        begin
                if (not judge(a[i,1],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]);
                        father[getfather(a[i,1])]:=getfather(a[i,2]);
                end;
        end;
        size[1]:=1;
        dfs(1,0);
        for j:=1 to trunc(ln(n)/ln(2)) do
        for i:=1 to n do
        begin
                anc[i,j]:=anc[anc[i,j-1],j-1];
                dis[i,j]:=max(dis[i,j-1],dis[anc[i,j-1],j-1]);
        end;
        for i:=1 to k do
        begin
                readln(x,y);
                writeln(lca(x,y));
        end;
end.

树链剖分code(44ms)

#include<cstdio>
#include<algorithm>
#define MAXN 15001
#define INF 1000000007

using namespace std;

int last[4*MAXN],next[4*MAXN],tov[4*MAXN],dis[4*MAXN];
int to_num[4*MAXN],father[MAXN];
int n,m,k,tot,total;

struct information
{
    int depth,father,size,heavy_son,top,to_tree,value;
}a[4*MAXN];

struct point
{
    int mx,ans;
}tree[4*MAXN];

struct dist
{
    int u,v,w;
}line[4*MAXN];

bool cmp(dist a,dist b)
{
    return a.w<b.w;
}

int getfather(int x)
{
    if (father[x]==0)return x;
    father[x]=getfather(father[x]);
    return father[x];
}

void insert(int x,int y,int z)
{
    next[++tot]=last[x];
    last[x]=tot;
    tov[tot]=y;
    dis[tot]=z;
}

void dfs1(int x,int father)
{
    int mx=-1;
    a[x].size=1;
    for (int i=last[x];i;i=next[i])
    {
        int j=tov[i];
        if (j!=father)
        {
            a[j].depth=a[x].depth+1;
            a[j].father=x;
            a[j].value=dis[i];
            dfs1(j,x);
            a[x].size+=a[j].size;
            if (a[j].size>mx)
            {
                mx=a[j].size;
                a[x].heavy_son=j;
            }
        }
    }
}

void dfs2(int x,int father,int top)
{
    if (x==0)return;
    a[x].top=top;
    a[x].to_tree=++total;
    to_num[total]=x;
    dfs2(a[x].heavy_son,x,top);
    for (int i=last[x];i;i=next[i])
    {
        int j=tov[i];
        if (j!=father && j!=a[x].heavy_son && j!=0)
        {
            dfs2(j,x,j);
        }
    }
}

void maketree(int t,int l,int r)
{
    if (l==r)
    {
        tree[t].mx=a[to_num[l]].value;
        return;
    }
    int mid=(l+r)/2;
    maketree(t*2,l,mid);
    maketree(t*2+1,mid+1,r);
    tree[t].mx=max(tree[t*2].mx,tree[t*2+1].mx);
}

int tree_query_max(int t,int l,int r,int x,int y)
{
    if (l==x && r==y)
    {
        return tree[t].mx;
    }
    int mid=(l+r)/2;
    if (y<=mid)
    {
        return tree_query_max(t*2,l,mid,x,y);
    }
    else if (x>mid)
    {
        return tree_query_max(t*2+1,mid+1,r,x,y);
    }
    else return max(tree_query_max(t*2,l,mid,x,mid),tree_query_max(t*2+1,mid+1,r,mid+1,y));
}

int query_max(int x,int y)
{
    int temp=-1;
    while (a[x].top!=a[y].top)
    {
        if (a[a[x].top].depth>a[a[y].top].depth)
        {
            temp=max(temp,tree_query_max(1,1,n,a[a[x].top].to_tree,a[x].to_tree));
            x=a[a[x].top].father;
        }
        else
        {
            temp=max(temp,tree_query_max(1,1,n,a[a[y].top].to_tree,a[y].to_tree));
            y=a[a[y].top].father;
        }
    }
    if (x==y)return temp;
    if (a[x].depth<a[y].depth)
    {
        temp=max(temp,tree_query_max(1,1,n,a[a[x].heavy_son].to_tree,a[y].to_tree));
    }
    else
    {
        temp=max(temp,tree_query_max(1,1,n,a[a[y].heavy_son].to_tree,a[x].to_tree));
    }
    return temp;
}

int main()
{
    //freopen("readin.txt","r",stdin);
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&line[i].u,&line[i].v,&line[i].w);
    }
    sort(line+1,line+m+1,cmp);
    for (int i=1;i<=m;i++)
    {
        if (getfather(line[i].u)!=getfather(line[i].v))
        {
            father[getfather(line[i].u)]=getfather(line[i].v);
            insert(line[i].u,line[i].v,line[i].w);
            insert(line[i].v,line[i].u,line[i].w);
        }
    }
    a[1].depth=1;
    dfs1(1,0),dfs2(1,0,1);
    maketree(1,1,n);
    for (int i=1;i<=k;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",query_max(x,y));
    }
    return 0;
}

不过其实对于随机数据的LCA,倍增没有什么意义

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值