Kruskal 重构树

很早以前学完 \(Kruskal\) 重构树(其实是去年),既然模拟赛考到了,那就整理一下吧。

相信阅读博客的你一定学过 \(Kruskal\) 求最小生成树的一种方法,具体来说:

Kruskal 算法是一种常见并且好写的最小生成树算法,由 Kruskal 发明.该算法的基本思想是从小到大加入边,是个贪心算法.
——源于 OI-Wiki.

也就是说 \(Kruskal\) 求最小生成树过程中会构建出很多个树模树样的连通块,不断地把当前没有使用过的最短边两端点所在的树连接起来,当所有的点第一次构成一个大树时就找到了最小生成树。

那么 \(Kruskal\) 就介绍完了,下面切入正题。

\(Kruskal\) 重构树就是指在使用 \(Kruskal\) 构建最小生成树过程中将每次连接用的边设做一个新建的虚点,点权为边权,每次加边的过程就是将边的两个端点视作虚点的儿子,挂在虚点下面。

不难发现,这样构建出来的树一定是一棵二叉树,因为一条边能且仅能连接两个节点。

\(Kruskal\) 重构树还有一些非常重要的性质:

不难发现,原图中两个点之间的所有简单路径上最大边权的最小值 = 最小生成树上两个点之间的简单路径上的最大值 = Kruskal 重构树上两点之间的 LCA 的权值.
——依旧源自 OI-Wiki.

解释一下就是两个点所有路径上最大边权的最小值(字面意思) \(=\) 最小生成树上两点路径上的最大值(很好理解,因为最小生成树上的边是从最小开始加的,边权一定是所有路径中最小的) \(=\) \(Kruskal\) 重构树上两点之间 \(LCA\) 的点权。

为什么等于 \(LCA\) 的点权呢?

因为 \(Kruskal\) 重构树的建立虚点操作是基于 \(Kruskal\) 的,而 \(Kruskal\) 是由小到大加边的;又因为建树时容易发现边权小的边所对应的点一定在大的下面,也就是说深度更深,所以两点在 \(Kruskal\) 重构树上的 \(LCA\) 很显然就是最小生成树中路径上的最大值。

例题:洛谷 P1967 [NOIP 2013 提高组] 货车运输

一张无向图,每次询问求 \(u\)\(v\) 的所有路径上的最小值的最大值。

挺板的,因为 \(Kruskal\) 重构树很好求两点所有简单路径上最大边权的最小值。那么考虑倒着加边就好了,让边权更大的边所对应的点在更下面的地方,很显然树上两点的 \(LCA\) 就是最大的最小值了(因为是基于最大生成树构建的重构树)。

#include<bits/stdc++.h>
using namespace std;
struct node{int d1,d2,val;};
node eg[50005];
vector<int> g[40005];
bool cmp(node x,node y){
    return x.val>y.val;
}
int fa[40005],f[40005][20],deep[40005],v[40005],flag[40005],sz;
int getfa(int u){
    return fa[u]==u?u:fa[u]=getfa(fa[u]);
}
void dfs(int u,int father){
	flag[u]=1;
	deep[u]=deep[father]+1,f[u][0]=father;
	for(int i=1;i<=15;i++) f[u][i]=f[f[u][i-1]][i-1];
	for(int i:g[u]){
		if(i==father) continue;
		dfs(i,u);
	}
}
int lca(int a,int b){
	if(deep[a]<deep[b]) swap(a,b);
	for(int i=15;i>=0;i--)
		if(deep[f[a][i]]>=deep[b]) a=f[a][i];
	if(a==b) return a;
	for(int i=15;i>=0;i--)
		if(f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i];
	return f[a][0];
}
int main(){
	int n,m;
    scanf("%d%d",&n,&m);
    sz=n;
    for(int i=1;i<=m;i++) scanf("%d%d%d",&eg[i].d1,&eg[i].d2,&eg[i].val);
    sort(eg+1,eg+m+1,cmp);
    for(int i=1;i<=2*n;i++) fa[i]=i;
    for(int i=1;i<=m;i++){
        int f1=getfa(eg[i].d1),f2=getfa(eg[i].d2);
        if(f1==f2) continue;
        int z=++sz;
        fa[f1]=fa[f2]=fa[z]=z,f[f1][0]=f[f2][0]=z,v[z]=eg[i].val;
        g[f1].push_back(z),g[f2].push_back(z),g[z].push_back(f1),g[z].push_back(f2);
    }
    for(int i=sz;i>=1;i--)
		if(flag[i]==0) dfs(i,0);
    int q;
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        int xx,yy;
        scanf("%d%d",&xx,&yy);
        if(getfa(xx)!=getfa(yy)) printf("-1\n");
        else printf("%d\n",v[lca(xx,yy)]);
    }
    return 0;
}

\(-\) ❀ 完结撒花 ❀ \(-\)

posted @ 2026-04-02 08:14  404-NoT_FinD  阅读(21)  评论(0)    收藏  举报