题解:[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;
}

浙公网安备 33010602011771号