版权声明:转载请附带原文链接,请勿随意删除原文内容,允许少量格式和/或内容修改,谢谢! https://blog.csdn.net/weixin_37661548/article/details/87688203
前言
- 感谢巨佬LRL52的帮助与指导。
参考代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005,MAXM = 100005;
struct edge
{
int to,nxt,wgt;
}info[MAXM << 1];
struct input
{
int from,to,wgt;
friend bool operator < (const input &opa,const input &opb)
{
return opa.wgt < opb.wgt;
}
}tmp[MAXM];
int n,m,k,e,ui,vi,wi,mst;
int head[MAXN],f[MAXN],dep[MAXN],fa[MAXN][20],maxPath[MAXN][20],lg[MAXN];
inline void addedge(int from,int to,int wgt)
{
info[++e].to = to;
info[e].wgt = wgt;
info[e].nxt = head[from];
head[from] = e;
}
void dfs(int u,int f,int d)
{
fa[u][0] = f;
dep[u] = d;
for (int i = 1;i <= lg[d];++i)
{
fa[u][i] = fa[fa[u][i - 1]][i - 1];
maxPath[u][i] = max(maxPath[u][i - 1],maxPath[fa[u][i - 1]][i - 1]);
}
for (int i = head[u];i;i = info[i].nxt)
{
int v = info[i].to;
if (v == f) continue;
maxPath[v][0] = info[i].wgt;
dfs(v,u,d + 1);
}
}
int LCA(int u,int v)
{
if (dep[u] < dep[v]) swap(u,v);
while (dep[u] != dep[v]) u = fa[u][lg[dep[u] - dep[v]]];
if (u == v) return u;
for (int i = lg[dep[u]];i >= 0;--i)
{
if (fa[u][i] != fa[v][i])
{
u = fa[u][i];
v = fa[v][i];
}
}
return fa[u][0];
}
int find(int opa)
{
return f[opa] == opa ? opa : f[opa] = find(f[opa]);
}
inline void merge(int opa,int opb)
{
f[find(opa)] = find(opb);
}
void kruskal()
{
int cnt = 0;
for (int i = 1;i <= m;++i)
{
int u = tmp[i].from;
int v = tmp[i].to;
int w = tmp[i].wgt;
if (find(u) == find(v)) continue;
merge(u,v);
addedge(u,v,w);
addedge(v,u,w);
mst += w;
cnt++;
}
}
int calc(int u,int v,int lca)
{
int res = -0x3f3f3f3f;
while (dep[u] != dep[lca])
{
res = max(res,maxPath[u][lg[dep[u] - dep[lca]]]);
u = fa[u][lg[dep[u] - dep[lca]]];
}
while (dep[v] != dep[lca])
{
res = max(res,maxPath[v][lg[dep[v] - dep[lca]]]);
v = fa[v][lg[dep[v] - dep[lca]]];
}
return res;
}
void init()
{
scanf("%d%d%d",&n,&m,&k);
for (int i = 1;i <= m;++i)
{
qread(tmp[i].from);
qread(tmp[i].to);
qread(tmp[i].wgt);
}
sort(tmp + 1,tmp + m + 1);
lg[0] = -1;
for (int i = 1;i <= n;++i)
{
lg[i] = lg[i >> 1] + 1;
f[i] = i;
}
}
void work()
{
kruskal();
dfs(1,0,0);
for (int i = 1;i <= k;++i)
{
qread(ui);
qread(vi);
if (find(ui) != find(vi))
{
printf("-1\n");
continue;
}
int lca = LCA(ui,vi);
int ans = calc(ui,vi,lca);
qwrite(ans);
putchar('\n');
}
}
int main()
{
init();
work();
return 0;
}
分析
-
最小瓶颈路,即图上两点 间的一条路径,满足这条路径上最大边权值在所有 的路径的最大边权值中最小。
-
最小瓶颈路一定存在于最小生成树上。作者并不会严格的数学证明,但网上似乎没有证明(或许是太显然了 ),作者觉得可以用Kruskal求最小生成树来伪证一发 :
已知Kruskal将图上所有边排序。
假如此时起点 与终点 位于两个不同的边集( ),加入边 ,使得起点和终点联通,由于事先将边排序,则边 必定在起点到终点的路径上权值最大的边(之一)。此时起点到终点之间有唯一路径。
假如存在边 ,连接后可以使得起点到终点之间的路径数增加,由于事先将边排序,则边 权值一定大于(等于)边 的权值,则边 是所有 的路径的最大边权值中最小的(之一)。
根据Kruskal求最小生成树的算法流程,加入的所有 条边组成最小生成树,则边 存在于最小生成树上,原命题得证。
-
那么,我们先用Kruskal求出MST(最小生成树),再利用 的LCA得到 的路径,并且在维护倍增数组时,维护节点 到其 级父亲节点之间的最长边权值即可。
当然,利用上面的证明,也可以用Kruskal求解本题,代码实现就简单很多了,所以作者就不给出代码。注意这只适用于数据比较小的情况。