[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;
}