\(Kruskal\)重构树 学习笔记
用途
\(Kruskal\)重构树可以维护树上两个点联通的最长边的最小值。
实现
首先我们要知道一个叫最小瓶颈生成树的东西,它和最小生成树的不同的地方是最小生成树是要求权值总和最小而最小瓶颈生成树是要求最大边最小。回顾\(Kruskal\)算法,我们可以发现用这种方法生成的最小生成树一定是最小瓶颈生成树。
对于\(Kruskal\)算法过程,我们发现如果当前边连接两个联通块\(A\)和\(B\),那么一定是\(A\)和\(B\)中的点互相联通的最长边的最小值。
有了以上结论我们发现我们可以将这个边值新建一个有权值的节点,这样如果以后要查询两个点联通的最长边的最小值只需要查它们的\(LCA\)的权值即可。
题目
#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\n",val[LCA(x,y)]); } return 0; }