Link。
我们注意到,整个题目的本质就是找到一条从 到 的路径,使得最小权值最大。
这是一条经典的最小瓶颈路问题。可以用 Kruskal 重构树或 Floyd 来解决。
因为 Floyd 是 的,虽然在这题可以通过,但是如果 的话,还是有必要掌握有关 Kruskal 重构树的知识的。
首先我们建立原图的 Kruskal 重构树,对于 Kruskal 重构树,有如下性质:
- Kruskal 重构树是一个二叉树。
- 按原图的最小生成树建立,则为一个大根堆,否则为一个小根堆。
- 按最大生成树建立,则任意两点的 LCA 点权即为两点之间路径最小值的最大值。
建法也很简单,跑普通的 Kruskal 最大生成树时以两点间的边权作为其父亲的点权,并查集中两点的父亲连到新的节点即可。
于是我们建完 Kruskal 重构树后,树剖单次 查询 LCA 即可。
在 次询问下的时间复杂度:,本题中因单次查询 。
#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 看能否到达。
复杂度是 的,这里不展示代码了。
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!