【题解】LuoGu1967:NOIP2013货车运输

本文介绍了一种解决货车在特定路网中寻找最大载重的算法。通过构建最大生成树,并利用并查集和倍增求LCA的方法,解决了货车运输过程中如何确定最大载重的问题。

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

原题传送门
题目描述

 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。
 现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入
 第一行有两个用一个空格隔开的整数 n,m,表示 A 国有n 座城市和 m 条道路。
 接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限
重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
 接下来一行有一个整数 q,表示有q 辆货车需要运货。
 接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,
注意:x 不等于 y。

输出
 输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。

样例输入
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
样例输出
3
-1
3
对于 100%的数据,0 < n <10,000,0 < m < 50,000,0< q < 30,000,0 ≤ z ≤ 100,000。

【题解】
由题意可知:每辆车的最大载重就是道路的最小值的最大值
那么要找到这个最小值的最大值就要用到最大生成树,又发现只有最大生成树的边是有用的,其他的便是没用的,这样使得整张图变成了森林(即不存在环)
最大生成树当然使用kruskal

构图已经会了,现在的问题是如何找出最小值的最大值。
首先判断-1的情况——若两点没联通,即输出-1。那么就用并查集判断就可以了,因为前面kruskal已经做好了
然后,想到暴力:从一个点走到另一个点
发现每辆车一定是跑最短路的,所以想到倍增求LCA

Code:

const maxn = 50010;
var
    edge:array[0..maxn*2] of record
        t,next,dis:longint;
    end;
    head,f,d,x,y,z:array[0..maxn*2] of longint;
    fa,w:array[0..maxn,0..30] of longint;
    vis:array[0..maxn] of boolean;
    n,m,q,i,num,xx,yy:longint;

function get(k:longint):longint;

begin
    if f[k] <> k then f[k] := get(f[k]);
    get := f[k];
end;

procedure swap(var x,y:longint);
var
    tmp:longint;

begin
    tmp := x; x := y; y := tmp;
end;

function min(x,y:longint):longint;

begin
    if x < y then exit(x) else exit(y);
end;

procedure sort(l,r:longint);
var
    i,j,mid:longint;

begin
    i := l; j := r; mid := z[(l + r) >> 1];
    repeat
        while z[i] > mid do inc(i);
        while z[j] < mid do dec(j);
        if i <= j then
        begin
            swap(x[i],x[j]);
            swap(y[i],y[j]);
            swap(z[i],z[j]);
            inc(i); dec(j);
        end;
    until i > j;
    if i < r then sort(i,r);
    if l < j then sort(l,j);
end;

procedure add(x,y,z:longint);//加边

begin
    inc(num);
    edge[num].t := y;
    edge[num].next := head[x];
    edge[num].dis := z;
    head[x] := num;
end;

procedure kruskal;
var
    i:longint;

begin
    for i := 1 to n do f[i] := i;
    for i := 1 to m do
        if get(x[i]) <> get(y[i]) then
        begin
            f[get(x[i])] := get(y[i]);
            add(x[i],y[i],z[i]); add(y[i],x[i],z[i]);//加边
        end;
end;

procedure dfs(u,pre:longint);
var
    i,e:longint;

begin
    vis[u] := true;
    d[u] := d[pre] + 1;//深度+1
    fa[u][0] := pre;//父亲
    i := 0;
    while fa[u][i] <> 0 do
    begin
        w[u][i+1] := min(w[u][i],w[fa[u][i]][i]);
        fa[u][i+1] := fa[fa[u][i]][i];
        inc(i);
    end;
    i := head[u];
    while i <> 0 do
    begin
        e := edge[i].t;
        if not vis[e] and (e <> pre) then//访问没访问过的点
        begin
            w[e][0] := edge[i].dis;
            dfs(e,u);
        end;
        i := edge[i].next;
    end;
end;

function lca(u,v:longint):longint;
var
    i:longint;

begin
    if get(u) <> get(v) then exit(-1);//-1的情况
    lca := maxlongint;
    if d[u] > d[v] then swap(u,v);
    for i := 20 downto 0 do
        if d[u] <= d[v] - (1 << i) then
        begin
            lca := min(lca,w[v][i]);
            v := fa[v][i];
        end;
    if u = v then exit(lca);
    for i := 20 downto 0 do
        if fa[u][i] <> fa[v][i] then
        begin
            lca := min(lca,min(w[u][i],w[v][i]));
            u := fa[u][i]; v := fa[v][i];
        end;
    lca := min(lca,min(w[u][0],w[v][0]));
end;

begin
    readln(n,m);
    for i := 1 to m do readln(x[i],y[i],z[i]);
    sort(1,m);
    kruskal;
    for i := 1 to n do
        if not vis[i] then
        begin
            w[i][0] := maxlongint;
            dfs(i,0);
        end;
    readln(q);
    for i := 1 to q do
    begin
        readln(xx,yy);
        writeln(lca(xx,yy));
    end;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值