用途
(Kruskal)重构树可以维护树上两个点联通的最长边的最小值。
实现
首先我们要知道一个叫最小瓶颈生成树的东西,它和最小生成树的不同的地方是最小生成树是要求权值总和最小而最小瓶颈生成树是要求最大边最小。回顾(Kruskal)算法,我们可以发现用这种方法生成的最小生成树一定是最小瓶颈生成树。
对于(Kruskal)算法过程,我们发现如果当前边连接两个联通块(A)和(B),那么一定是(A)和(B)中的点互相联通的最长边的最小值。
有了以上结论我们发现我们可以将这个边值新建一个有权值的节点,这样如果以后要查询两个点联通的最长边的最小值只需要查它们的(LCA)的权值即可。
题目
Network
#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
const int N=2e5+100,M=3e5+100;
struct edge{
int s,e,v,net;
}ed[N<<1],id[M];
int n,m,tot,k;
int head[N<<1],size[N<<1],deep[N<<1],top[N<<1],father[N<<1],son[N<<1],f[N<<1],val[N<<1];
inline bool cmp(edge a,edge b) {return a.v<b.v;}
inline int getf(int x) {return f[x]==x ? x:f[x]=getf(f[x]);}
inline int LCA(int x,int y)
{
while (top[x]!=top[y])
{
if (deep[top[x]]>deep[top[y]]) swap(x,y);
y=father[top[y]];
}
return deep[x]<deep[y] ? x:y;
}
inline void dfs2(int x,int tp)
{
top[x]=tp;
if (son[x]) dfs2(son[x],tp);
for (int i=head[x];i;i=ed[i].net)
if (ed[i].e!=father[x]&&ed[i].e!=son[x])
dfs2(ed[i].e,ed[i].e);
return ;
}
inline void dfs1(int x,int fa)
{
deep[x]=deep[fa]+1;
father[x]=fa;
size[x]=1;
for (int i=head[x];i;i=ed[i].net)
if (ed[i].e!=fa)
{
dfs1(ed[i].e,x);
size[x]+=size[ed[i].e];
if (size[son[x]]<size[ed[i].e])
son[x]=ed[i].e;
}
return ;
}
inline void add(int s,int e)
{
ed[++tot]=(edge){s,e,0,head[s]};
head[s]=tot;
return ;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=m;i++)
{
int s,e,v;
scanf("%d%d%d",&s,&e,&v);
id[i]=(edge){s,e,v,0};
}
sort(id+1,id+m+1,cmp);
for (int i=1;i<=n*2;i++)
f[i]=i;
int num=0,cnt=n;
for (int i=1;i<=m;i++)
{
int a=getf(id[i].s),b=getf(id[i].e);
if (a!=b)
{
f[a]=f[b]=++cnt;
add(cnt,a);add(cnt,b);
val[cnt]=id[i].v;
num++;
if (num==n-1) break;
}
}
dfs1(cnt,0);
dfs2(cnt,cnt);
while (k--)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d
",val[LCA(x,y)]);
}
return 0;
}
P4768 [NOI2018]归程
注意事项
- (Kruskal) 重构树是对边建立一个新的点,所以可以说是将边权转变为点权
- (Kruskal) 重构树因为新建了节点,所以节点数要乘 (2) ,并查集也要初始化两倍,并查集合并的父亲为这两个点重构的新点