【bzoj 3732】Network

本文介绍了一种解决货车运输问题的算法实现,首先通过求最小生成树确定最优路径,然后利用倍增法求最近公共祖先(LCA)。该算法特别之处在于更新节点前先更新最值,避免使用已更新的节点再次更新最值导致错误。

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

注意:此题为货车运输的变式,要先更新最值,在更新节点,不然用更新以后的节点再更新最值就不对了。

先求最小生成树,再倍增求lca。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 30010;
const int Maxn = 15010;
struct edge{
    int from, to, dis;
};
int _first[Maxn], _next[maxn], _to[maxn], _dis[maxn], cnt;
int fa[Maxn], deep[Maxn], _fa[Maxn];
int up[Maxn][25], max_dis[Maxn][25];
edge e[maxn];
bool cmp(edge a, edge b){
    return a.dis < b.dis;
}
int find(int x){
    if(_fa[x] == x) return x;
    return _fa[x] = find(_fa[x]);
}
void add(int a, int b, int c){
    _next[++cnt] = _first[a];
    _first[a] = cnt;
    _to[cnt] = b;
    _dis[cnt] = c;
}
void dfs(int now, int dep){
    deep[now] = dep;
    up[now][0] = fa[now];
    for(int i = 1; i <= 20; i ++){
        up[now][i] = up[up[now][i-1]][i-1];
        max_dis[now][i] = max(max_dis[now][i-1], max_dis[up[now][i-1]][i-1]);
    }
    for(int i = _first[now]; i; i = _next[i]){
        if(_to[i] != fa[now]){
            fa[_to[i]] = now;
            max_dis[_to[i]][0] = _dis[i];
            dfs(_to[i], dep+1);
        }
    }
}
int lca(int a, int b){
    int max1 = 0, max2 = 0;
    if(deep[a] != deep[b]){
        if(deep[a] < deep[b]) swap(a,b);
        while(deep[fa[a]] != deep[b]){
            int i = 0;
            while(deep[up[a][i]] > deep[b]) i ++;
            i --;
            max1 = max(max1, max_dis[a][i]);
            a = up[a][i]; 
        }
        max1 = max(max1, max_dis[a][0]);
        a = fa[a];
    }
    if(a == b) return max(max1, max2);
    while(fa[a] != fa[b]){
        int i = 0;
        while(up[a][i] != up[b][i]) i ++;
        i --;
        max1 = max(max1, max_dis[a][i]);
        max2 = max(max2, max_dis[b][i]);
        a = up[a][i], b = up[b][i];
    }
    return max(max(max1, max_dis[a][0]), max(max2, max_dis[b][0])); 
}
int main(){
    int n, m, k;
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= n; i ++) _fa[i] = i;
    memset(max_dis, 128, sizeof max_dis);
    for(int i = 1; i <= m; i ++){
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        e[i] = (edge){a,b,c};
    }
    sort(e+1, e+1+m, cmp);
    for(int i = 1, num = 0;i<=m&& num < n; i ++){
        if(find(e[i].from) != find(e[i].to)){
            _fa[_fa[e[i].from]] = e[i].to;
            add(e[i].from, e[i].to, e[i].dis);
            add(e[i].to, e[i].from, e[i].dis);
            num ++;
        }
    }
    fa[1] = 1;
    dfs(1,1);
    for(int i = 1; i <= k; i ++){
        int a, b;
        scanf("%d%d", &a, &b);
        printf("%d\n", lca(a,b));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值