原题链接:https://www.luogu.org/problem/show?pid=1967
对于这个题,需要用到一些最小生成树的一些性质。
对于一个图的任意两棵最小生成树,每条边的权值都是一一对应的。
对于一个图的所有生成树,任意两点之间的路径(每棵树上唯一确定),最小生成树上,最长的边最短。
最大生成树反之亦然。任意两点之间,最短的边最长。
此题为双向图,在边权问题上,与无向图没什么区别。。。
于是对于每一组询问的两个节点,求出他们的LCA,保存两个点分别走到LCA时的边权最小的权值。
如果两个点不在同一棵树内,当然不会有LCA。
然后就是LCA板子啥的了,蒟蒻码力太弱,写了好长。。。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; void read(int &y)//快速读入 { y=0;char x=getchar(); while(x<'0'||x>'9') x=getchar(); while(x>='0'&&x<='9') { y=y*10+x-'0'; x=getchar(); } } struct edge { int u,v,w; }e[50005]; bool cmp(edge x,edge y)//最大生成树边权可从大到小排序 { return x.w>y.w; } int n,m,q,cnt; int head[20005],nxt[20005],to[20005],d[20005],f[10005],dep[10005]; int fa[10005][25],minv[10005][25]; void add(int u,int v,int w)//邻接表存边 { to[cnt]=v; d[cnt]=w; nxt[cnt]=head[u]; head[u]=cnt++; } int find(int x)//并查集 { if(x==f[x]) return x; return f[x]=find(f[x]); } void dfs(int x,int y)//dfs找出每个点的深度 { for(int i=head[x];i!=-1;i=nxt[i]) { if(i!=y) { dep[to[i]]=dep[x]+1; fa[to[i]][0]=x; minv[to[i]][0]=d[i]; dfs(to[i],i^1); } } } int lca(int x,int y)//LCA { if(dep[x]>dep[y]) { int t=x; x=y; y=t; } int ans=(1<<30); for(int i=15;i>=0;i--) { if(dep[fa[y][i]]>=dep[x]) { ans=min(ans,minv[y][i]); y=fa[y][i]; } } if(x==y) return ans; for(int i=15;i>=0;i--) { if(fa[x][i]!=fa[y][i]) { ans=min(ans,min(minv[x][i],minv[y][i])); x=fa[x][i]; y=fa[y][i]; } } return fa[x][0]==0 ? -1 : min(ans,min(minv[x][0],minv[y][0])); } int main() { read(n);read(m); int u,v,w; for(int i=0;i<m;i++) { read(e[i].u);read(e[i].v);read(e[i].w); } memset(head,-1,sizeof(head)); sort(e,e+m,cmp); for(int i=1;i<=n;i++) f[i]=i; for(int i=0;i<m;i++) { if(find(e[i].u)!=find(e[i].v)) { add(e[i].u,e[i].v,e[i].w); add(e[i].v,e[i].u,e[i].w); f[find(e[i].u)]=find(e[i].v); } } dep[1]=1;dfs(1,-1); for(int j=1;j<=15;j++) { for(int i=1;i<=n;i++) { fa[i][j]=fa[fa[i][j-1]][j-1]; minv[i][j]=min(minv[i][j-1],minv[fa[i][j-1]][j-1]); } } int q,x,y;read(q); for(register int i=0;i<q;i++) { read(x);read(y); printf("%d ",lca(x,y)); } return 0; }