LOADING

加载过慢请开启缓存 浏览器默认开启

kruskal重构并倍增求路径最小值

[NOIP2013 提高组] 货车运输

题目背景

NOIP2013 提高组 D1T3

题目描述

A 国有 $n$ 座城市,编号从 $1$ 到 $n$,城市之间有 $m$ 条双向道路。每一条道路对车辆都有重量限制,简称限重。

现在有 $q$ 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入格式

第一行有两个用一个空格隔开的整数 $ n,m$,表示 A 国有 $ n$ 座城市和 $m$ 条道路。

接下来 $m$ 行每行三个整数 $x, y, z$,每两个整数之间用一个空格隔开,表示从 $x $ 号城市到 $ y $ 号城市有一条限重为 $z$ 的道路。
注意: $x \neq y$,两座城市之间可能有多条道路 。

接下来一行有一个整数 $q$,表示有 $q$ 辆货车需要运货。

接下来 $q$ 行,每行两个整数 $x,y$,之间用一个空格隔开,表示一辆货车需要从 $x$ 城市运输货物到 $y$ 城市,保证 $x \neq y$

输出格式

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

样例输入 #1

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

样例输出 #1

3
-1
3

实现

​ 最初的想法是tarjan缩点,去环后倍增求最小值,仔细思考后发现是完全没有道理的,无法在多条路线里面找到最优的路线,所以转变为用生成树重构图确定唯一的路线后再去倍增求最小值

#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int inf32 = 1e9 + 7;
constexpr int inf64 = 1e9 + 7;
const int N=2e5+10;
typedef pair<int,int> PII;
int n,m;
struct tree{
    int u,v,w;
    bool operator< (const tree &t) const{
        return w>t.w;
    }
}edge[N];
vector<PII>e[N];
int p[N],ans,cnt;
int find(int x){
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
int per[N][32];
int val[N][32];
int dep[N];
int st[N];
void dfs(int u,int f){
    dep[u]=dep[f]+1;
    st[u]=1;
    for(auto [v,w]:e[u]){
        if(v==f) continue;
        per[v][0]=u;
        val[v][0]=w;
        dfs(v,u);
    }
}
void kruskal(){
    sort(edge+1,edge+1+m);          //根据边权排序,由大到小枚举
    for(int i=1;i<=m;i++){
        int x=find(edge[i].u);
        int y=find(edge[i].v);
        if(x!=y){                   //若父节点与子节点在同一集合则不可连边,否则出现环
            e[edge[i].u].push_back({edge[i].v,edge[i].w});
            e[edge[i].v].push_back({edge[i].u,edge[i].w});
            p[x]=y;
            ans+=edge[i].w;
            cnt++;                  //重构后的树点的个数加一
        }
    }
}
int query(int u,int v){             //倍增lca求路径最小值
    int ans=1e9;
    if(dep[u]>dep[v]) swap(u,v);
    int d=dep[v]-dep[u];
    for(int j=20;j>=0;j--){
        if(d&(1<<j)){
            ans=min(ans,val[v][j]);
            v=per[v][j];
        }
    }
    if(u==v){
        return ans;
    }
    for(int j=20;j>=0;j--){
        if(per[u][j]!=per[v][j]){
            ans=min(ans,min(val[u][j],val[v][j]));
            u=per[u][j];
            v=per[v][j];
        }
    }
    ans=min(ans,min(val[u][0],val[v][0]));
    return ans;
}
void solve(){
    //int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        edge[i]={u,v,w};
    }
    for(int i=1;i<=n;i++){
        p[i]=i;
    }
    kruskal();
    for(int i=1;i<=n;i++){
        if(!st[i]) dfs(i,0);
    }
    //dfs(1,0);
    for(int j=1;j<32;j++){          //构建st表
        for(int i=1;i<=n;i++){
            per[i][j]=per[per[i][j-1]][j-1];
            val[i][j]=min(val[i][j-1],val[per[i][j-1]][j-1]);
        }
    }
    int q;
    cin>>q;
    for(int i=1;i<=q;i++){
        int u,v;
        cin>>u>>v;
        if(find(u)!=find(v)){
            cout<<-1<<endl;
        }
        else{
            cout<<query(u,v)<<endl;
        }
    }
}    
signed main(){
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
}