题解:[NOIP2013 提高组] 货车运输

题目传送门

题意分析

为了使两座城市间的道路限重的最小值尽可能大,那么我们建立一棵最大生成树即可。

然后对于每次询问 \(x,y\),输出 \(x\sim \operatorname{lca}(x,y),\operatorname{lca}(x,y)\sim y\) 的限重最小值即可。

问题就在于如何存储最小值。

事实上,这个问题很简单:只需要在倍增LCA时存储 \(x\)\(x\)\(2^i\) 级祖先的最小值即可,具体实现参照代码。

AC代码

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
const int N=1e4,M=5e4;
struct edge{
	int u,v,w;
}x[M+1];
struct edge_tree{
	int v,r,w;
}a[2*M+1];
int n,m,u,v,w,q,f[N+1],h[N+1],lg[N+1],d[N+1],pl[N+1][31],dis[N+1][31];
bool vis[N+1];
//邻接表
void create(int u,int v,int w){
	static int top=0;
	a[++top]={v,h[u],w};
	h[u]=top;
}//并查集
int find(int x){
	if(f[x]!=x)return f[x]=find(f[x]);
	return x;
}
void unite(int x,int y){
	f[find(x)]=find(y);
}
bool cmp(edge a,edge b){
	return a.w>b.w;
}//Kruskal最大生成树
void Kruskal(){
	sort(x+1,x+m+1,cmp);
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1,cnt=0;i<=m&&cnt<n-1;i++){
		if(find(x[i].u)!=find(x[i].v)){
			unite(x[i].u,x[i].v);
			create(x[i].u,x[i].v,x[i].w);
			create(x[i].v,x[i].u,x[i].w);
		}
	}
}//LCA预处理
void dfs(int p,int q,int w){
	vis[p]=true;
	pl[p][0]=q;
	dis[p][0]=w;
	d[p]=d[q]+1;
	for(int i=1;i<=lg[d[p]];i++){
		pl[p][i]=pl[pl[p][i-1]][i-1];
		dis[p][i]=min(dis[p][i-1],dis[pl[p][i-1]][i-1]);
	}for(int i=h[p];i>0;i=a[i].r){
		if(a[i].v!=q)dfs(a[i].v,p,a[i].w);
	}
}//倍增LCA
int lca(int u,int v){
	if(find(u)!=find(v))return -1;
	int ans=2147483647;
	if(d[u]<d[v])swap(u,v);
	while(d[u]>d[v]){
		ans=min(ans,dis[u][lg[d[u]-d[v]]-1]);
		u=pl[u][lg[d[u]-d[v]]-1];
	}if(u==v)return ans;
	for(int i=lg[d[u]]-1;i>=0;i--){
		if(pl[u][i]!=pl[v][i]){
			ans=min(ans,min(dis[u][i],dis[v][i]));
			u=pl[u][i];
			v=pl[v][i];
		}
	}ans=min(ans,min(dis[u][0],dis[v][0]));
	return ans;
}
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++)scanf("%d %d %d",&x[i].u,&x[i].v,&x[i].w);
	Kruskal();
	for(int i=1;i<=n;i++)lg[i]=lg[i/2]+1;
	for(int i=1;i<=n;i++){
		if(vis[i]==false)dfs(i,0,0);
	}
	scanf("%d",&q);
	while(q--){
		scanf("%d %d",&u,&v);
		printf("%d\n",lca(u,v));
	}
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2025-07-20 11:34  TH911  阅读(8)  评论(0)    收藏  举报