P1967 货车运输 ( 倍增 + 最大生成树 )

题目链接

题目描述:
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。
现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入格式:
第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行三个整数 x, y, z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。
注意: x != y,两座城市之间可能有多条道路 。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x,y 之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,保证 x != y
输出格式:
共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。
如果货车不能到达目的地,输出 -1。
数据范围:
1 <= n <=1e4 1 <= m <=1e5 1 <= q <=3e4 1<= z <=1e5
解题报告: 无向带权图
题目是求两点之间任意可达路径上最小边权的最大值,所以要想办法选取尽量边权值大的边进行运输,那么权值较小的应该被舍弃。不妨够造最大生成树,得到最大生成树后,我们可以树上倍增求出任意两节点的最小载重量。
注意判断不可达情况即不在同一联通块。

#define first f
#define second s
#define ll long long
#define mp make_pair
#define pb push_back
#define pf push_front
#define lb lower_bound
#define ub upper_bound
#include <bits/stdc++.h>
#define pii pair<int,int>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=5e4+5;
const int MOD=1e9+7;
const double PI=acos(-1);
const double e=2.718281828459;

struct node
{
    int x,y,z;
}p[maxn];
vector<pii>edge[maxn];
int n,m,q;
bool vis[maxn];
int deep[maxn],f[maxn];
int ans[maxn][25],fa[maxn][25];

bool cmp(node a,node b){return a.z>b.z;}
int getfind(int x)
{
    return x==f[x]?x:f[x]=getfind(f[x]);
}
void Kruskal()
{
    sort(p+1,p+m+1,cmp);
    for(int i=1;i<=n;i++){f[i]=i;}
    for(int i=1;i<=m;i++){
        int k1=getfind(p[i].x);
        int k2=getfind(p[i].y);
        if(k1!=k2){
            f[k2]=k1;
            edge[p[i].x].pb({p[i].y,p[i].z});
            edge[p[i].y].pb({p[i].x,p[i].z});
        }
    }
}

void dfs(int now)
{
    vis[now]=true;
    for(int i=0;i<edge[now].size();i++){
        pii pp=edge[now][i];
        if(vis[pp.f]){continue;}
        fa[pp.f][0]=now;
        deep[pp.f]=deep[now]+1;
        ans[pp.f][0]=pp.s;
        dfs(pp.f);
    }
}
int LCA_RMQ(int x,int y)
{
    int mi=1e9+7;
    if(deep[x]<deep[y]){swap(x,y);}
    for(int i=20;i>=0;i--){
        if(deep[fa[x][i]]>=deep[y]){
            mi=min(mi,ans[x][i]);
            x=fa[x][i];
        }
    }
    if(x==y){return mi;}
    for(int i=20;i>=0;i--){
        if(fa[x][i]!=fa[y][i]){
            mi=min(mi,ans[x][i]);
            mi=min(mi,ans[y][i]);
            x=fa[x][i];y=fa[y][i];
        }
    }
    return min(mi,min(ans[x][0],ans[y][0]));
}
int main()
{
    mem(vis,false);
    int x,y,z;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        p[i]={x,y,z};
    }
    Kruskal();
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            deep[i]=1;
            dfs(i);
            ans[i][0]=MOD;
            fa[i][0]=i;

        }
    }
    for(int i=1;i<=20;i++){
        for(int j=1;j<=n;j++){
            fa[j][i]=fa[fa[j][i-1]][i-1];
            ans[j][i]=min(ans[j][i-1],ans[fa[j][i-1]][i-1]);
        }
    }
    scanf("%d",&q);
    while(q--){
        scanf("%d%d",&x,&y);
        if(getfind(x)!=getfind(y)){printf("-1\n");}
        else{printf("%d\n",LCA_RMQ(x,y));}
    }
    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值