[NOIP2013 D1T3]货车运输 [LCA][最大生成树]

[NOIP2013 D1T3]货车运输 [LCA][最大生成树]

问题描述

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。

解法

给出一张可能有重边的图,找到一条路径,使得该路径上的最小限重的道路的限重最大。可能会想到二分,但是这种解法非常麻烦。不过我们可以事先把限重最大的道路挑出来再找点之间的最大限重,会方便很多。因此会有一个最大生成树的预处理。接下来就可以用LCA找每个点之间的最小的限重(每个点之间有且只有一条路径,如果要满足所有的道路,必须要满足限重最小的那条路)。

由于给出的图可能不连通,构不成一棵树,所以要用并查集判断两个点是否在同一棵树上。

代码

#include<iostream>
#include<cstdio>
#include<map>
#include<queue>
#include<cmath>
#define E 500100
#define N 100100
using namespace std;
struct node{
    int x,y;
    bool operator<(const node &a)const{
        if(y==a.y)return x<a.x;
        return y<a.y;
    }//限重大的边优先
};
map<node,int>Mark;
struct Node{
    int x,y,w;
    bool operator<(const Node &a)const{
        return w<a.w;
    }
};
int End[E],Len[E],Next[E],Last[E],cnt;
void Ins(int x,int y,int w){
    End[++cnt]=y,Len[cnt]=w;
    Next[cnt]=Last[x],Last[x]=cnt;
}
int _Fa[N],n,m;
int getFa(int x){
    if(_Fa[x]!=x)_Fa[x]=getFa(_Fa[x]);
    return _Fa[x];
}
priority_queue<Node>q;
void MaxTree(){
    while(!q.empty()){
        Node t=q.top();q.pop();
        int xx=getFa(t.x),yy=getFa(t.y);
        if(xx>yy)swap(xx,yy);
        if(xx!=yy)_Fa[xx]=yy,Ins(t.x,t.y,t.w),Ins(t.y,t.x,t.w);
    }
}
int D[N],Fa[N][20],Dis[N][20];bool used[N];
void DFS(int u){//找限重最小的边
    D[u]=D[Fa[u][0]]+1,used[u]=true;
    int s=ceil(log2(D[u]));
    for(int i=1;i<=s;i++){
        Fa[u][i]=Fa[Fa[u][i-1]][i-1];
        Dis[u][i]=min(Dis[u][i],min(Dis[u][i-1],Dis[Fa[u][i-1]][i-1]));
    }
    for(int i=Last[u];i;i=Next[i])
        if(!used[End[i]]){
            Fa[End[i]][0]=u,Dis[End[i]][0]=Len[i];
            DFS(End[i]);
        }
}
int Find(int x,int y){
    if(D[x]<D[y])swap(x,y);
    int Ans=1e9;
    int s=ceil(log2(D[x]));int d=D[x]-D[y];
    for(int i=0;i<=s;i++)
        if(d&(1<<i))Ans=min(Ans,Dis[x][i]),x=Fa[x][i];
    if(x==y)return Ans;
    for(int i=s;i>=0;i--)
        if(Fa[x][i]!=Fa[y][i]){
            Ans=min(Ans,min(Dis[x][i],Dis[y][i]));
            x=Fa[x][i],y=Fa[y][i];
        }
    return min(Ans,min(Dis[x][0],Dis[y][0]));
}
bool Used[N];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        if(z>Mark[node{x,y}]){
            q.push(Node{x,y,z});
            Mark[node{x,y}]=z,Mark[node{y,x}]=z;
        }
    }
    for(int i=1;i<=n;i++){
        _Fa[i]=i;
        for(int j=0;j<20;j++)Dis[i][j]=1e9;
    }
    MaxTree();
    for(int i=1;i<=n;i++){
        int t=getFa(i);
        if(!Used[t])Used[t]=true,DFS(t);
    }
    int q;scanf("%d",&q);
    for(int i=1;i<=q;i++){
        int x,y;scanf("%d%d",&x,&y);
        if(getFa(x)!=getFa(y))puts("-1");
        else printf("%d\n",Find(x,y));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/arliastark/article/details/80660554