【题解】UVA10099 The Tourist Guide 【Kruskal 重构树】

1|0The Tourist Guide

1|1题目

Link

1|2题解

我们注意到,整个题目的本质就是找到一条从 st 的路径,使得最小权值最大。

这是一条经典的最小瓶颈路问题。可以用 Kruskal 重构树或 Floyd 来解决。

因为 Floyd 是 Θ(n3) 的,虽然在这题可以通过,但是如果 1n2×105 的话,还是有必要掌握有关 Kruskal 重构树的知识的。

首先我们建立原图的 Kruskal 重构树,对于 Kruskal 重构树,有如下性质:

  1. Kruskal 重构树是一个二叉树。
  2. 按原图的最小生成树建立,则为一个大根堆,否则为一个小根堆。
  3. 按最大生成树建立,则任意两点的 LCA 点权即为两点之间路径最小值的最大值。

建法也很简单,跑普通的 Kruskal 最大生成树时以两点间的边权作为其父亲的点权,并查集中两点的父亲连到新的节点即可。

于是我们建完 Kruskal 重构树后,树剖单次 Θ(logn) 查询 LCA 即可。

q 次询问下的时间复杂度:Θ(mlogm+qlogn),本题中因单次查询 q=1

1|3Code

#include<bits/stdc++.h> #define DEBUG(x) puts(#x) inline int read() { int x(0),f(0); char ch=getchar(); for(; !isdigit(ch); ch=getchar()) f|=(ch=='-'); for(; isdigit(ch); ch=getchar()) x=(x<<1)+(x<<3)+(ch^48); return f?-x:x; } const int N=5e5+5; struct graph { int u,v,w; bool operator <(const graph rhs) const { return w > rhs.w; } } edge[N]; int n,m,T; void so1ve(); int main() { while(scanf("%d%d",&n,&m)==2 && n!=0 && m!=0) so1ve(); return 0; } class dsu { private: int fa[N]; public: void mst(int M) { std::iota(fa+1,fa+M+1,1); } int find(int x) { if(x==fa[x]) return x; return fa[x]=find(fa[x]); } void merge(int x,int y) { int fx=find(x),fy=find(y); if(fx==fy) return; fa[fx]=fy; return; } bool same(int x,int y) { return find(x)==find(y); } void replace(int x,int val) { fa[find(x)]=val; return; } } _dsu; struct linked_star { int to,nxt; } G[N<<1]; int head[N],cnt=0; void addEdge(int u,int v) { G[++cnt]= {v,head[u]}; head[u]=cnt; } std::array<int,N> fa,son,dep,siz,top,val,vis; void dfs1(int x) { dep[x]=dep[fa[x]]+1; siz[x]=1; vis[x]=1; for(int i=head[x]; i; i=G[i].nxt) { int t=G[i].to; if(t==fa[x]) continue; fa[t]=x; dfs1(t); siz[x]+=siz[t]; if(!son[x] || siz[t]>siz[son[x]]) son[x]=t; } } void dfs2(int x,int t) { top[x]=t; if(son[x]) dfs2(son[x],t); for(int i=head[x]; i; i=G[i].nxt) { int to=G[i].to; if(to==fa[x] || to==son[x]) continue; dfs2(to,to); } } int getlca(int x,int y) { while(top[x]!=top[y]) { if(dep[top[x]]>=dep[top[y]]) x=fa[top[x]]; else y=fa[top[y]]; } return (dep[x]>dep[y]?val[y]:val[x]); } void ex_kruskal() { std::sort(edge+1,edge+m+1); int id=n,lim=n<<1; _dsu.mst(lim+5); for(int i=1; i<=m; i++) { int u=_dsu.find(edge[i].u),v=_dsu.find(edge[i].v),w=edge[i].w; if(u==v) continue; ++id; _dsu.replace(u,id); _dsu.replace(v,id); val[id]=w; addEdge(u,id), addEdge(id,u); addEdge(v,id), addEdge(id,v); if(id==lim-1) break; } for(int i=1; i<=id; i++) if(!vis[i]) dfs1(_dsu.find(i)),dfs2(_dsu.find(i),_dsu.find(i)); } int testcase; void so1ve() { for(int i=1; i<=m; i++) { int u,v,w; u=read(), v=read(), w=read(); edge[i]= {u,v,w}; } ex_kruskal(); int s,t,sum,ans; s=read(), t=read(), sum=read(); int p=getlca(s,t)-1; if(sum%p==0) ans=sum/p; else ans=sum/p+1; printf("Scenario #%d\nMinimum Number of Trips = %d\n\n", ++testcase,ans); for(int i=1; i<=m; i++) edge[i]= {0,0,0}; for(int i=1; i<=cnt; i++) G[i]= {0,0}; for(int i=1; i<=n<<1; i++) head[i]=val[i]=fa[i]=son[i]=top[i]=siz[i]=dep[i]=vis[i]=0; cnt=0; }

注意换行和多测清空

注意到,本题还可以二分一个边权的最大值,跑 Dijkstra 看能否到达。

复杂度是 Θ((n+m)log2n) 的,这里不展示代码了。


__EOF__

本文作者TheSky233
本文链接https://www.cnblogs.com/TheSky233/p/17041341.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   TheSky233  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示