Kruskal重构树学习笔记

引言

其实很早就学了,但是用的不多一直没有补。做题时突然,想起来了补一下。

感觉Kruskal重构树也算一种数据节构吧。为什么要单独拎出来讲呢?因为感觉博客园里这个知识点讲的都不说很清楚,大家好像都对这个知识点嗤之以鼻(?原谅我语文不好)。新开一个会好找一点。 最后无耻宣一下:数据节构专题学习笔记

基本信息

  • 性质

    在这棵树上节点u到节点v的LCA是原图u到v路径上边权最值。且如果原图连通这棵树保证为二叉树,共有 \(2N-1\) 个节点。从小到大构建时,父亲权值不小于儿子;从大到小构建时,父亲权值不大于儿子。

    其实此类问题也可以用并查集按秩合并+可持久化来维护,但重构树的优点在于可以套树剖、线段树等静态树结构。

  • 建树过程:
    类似执行最小生成树算法(Kruskal) ,但不直接连边,而是在新图中创建节点代表这条边。具体过程:遍历每条边(u,v,w) 并维护一个并查集。如果uv不联通:新建节点。在并查集里找到u和v的根节点:ru,rv。新建节点将ru和rv合并,把根节点设为该节点。这棵树就是Kruskal重构树。

  • code

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=3e5*2+10;
    struct EDGE{int u,v,w;}edge[N];int tot=0;
    void addedge(int u,int v,int w){edge[++tot]={u,v,w};}
    bool cmp(EDGE x,EDGE y){return x.w>y.w;}
    int fa[N],w[N],trfa[N],dep[N],lg[N];int stfa[N][20];
    int fid(int x) {if(x!=fa[x])fa[x]=fid(fa[x]);return fa[x];}
    void init(int n,int m) {
    	for(int i=2;i<3e5;i++)lg[i]=lg[i/2]+1;
    	for(int i=1;i<=n+m;i++){trfa[i]=i;fa[i]=i;}
    }
    int LCA(int u,int v) {
    	if(dep[u]<dep[v])swap(u,v);
    	while(dep[u]>dep[v]){int k=lg[dep[u]-dep[v]];u=stfa[u][k];}
    	if(u==v) return u;
    	for(int k=lg[dep[u]];k>=0;k--) 
    		if(stfa[u][k]!=stfa[v][k]){u=stfa[u][k];v=stfa[v][k];}
    	return trfa[u];
    }
    signed main() {
    	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    	int n,m; cin>>n>>m; init(n,m) ;
    	for(int i=1,u,v;i<=m;i++){cin>>u>>v>>w[i]; addedge(u,v,w[i]);}
    	//-----------建树过程-----------
    	sort(edge+1,edge+1+m,cmp) ;
    	for(int i=1;i<=m;i++) {
    		int u=edge[i].u,v=edge[i].v;
    		int fau=fid(u),fav=fid(v);
    		if(fau==fav) continue;
    		n++ ;
    		w[n]=edge[i].w;
    		trfa[fau]=trfa[fav]=fa[fau]=fa[fav]=n;
    	} // 在重构树中加入新节点
    	for(int i=n;i>=1;i--) dep[i]=dep[trfa[i]]+1;
    	for(int i=1;i<=n;i++) stfa[i][0]=trfa[i];
    	for(int i=1;i<=19;i++)
    		for(int u=1;u<=n;u++) 
    			stfa[u][i]=stfa[stfa[u][i-1]][i-1]; //初始化
    	int Q; cin>>Q;
    	while(Q--) {
    		int u,v; cin>>u>>v;
    		if(fid(u)!=fid(v)) cout<<"-1\n";
    		else cout<<w[LCA(u,v)]<<"\n"; // 朴素LCA
    	}
    	//-----------EOF-----------
    	return 0; 
    }
    
    高清无注释版
    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=3e5*2+10;
    struct EDGE{int u,v,w;}edge[N];int tot=0;
    void addedge(int u,int v,int w){edge[++tot]={u,v,w};}
    bool cmp(EDGE x,EDGE y){return x.w>y.w;}
    int fa[N],w[N],trfa[N],dep[N],lg[N];int stfa[N][20];
    int fid(int x) {if(x!=fa[x])fa[x]=fid(fa[x]);return fa[x];}
    void init(int n,int m) {
    	for(int i=2;i<3e5;i++)lg[i]=lg[i/2]+1;
    	for(int i=1;i<=n+m;i++){trfa[i]=i;fa[i]=i;}
    }
    int LCA(int u,int v) {
    	if(dep[u]<dep[v])swap(u,v);
    	while(dep[u]>dep[v]){int k=lg[dep[u]-dep[v]];u=stfa[u][k];}
    	if(u==v) return u;
    	for(int k=lg[dep[u]];k>=0;k--) 
    		if(stfa[u][k]!=stfa[v][k]){u=stfa[u][k];v=stfa[v][k];}
    	return trfa[u];
    }
    signed main() {
    	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    	int n,m; cin>>n>>m; init(n,m) ;
    	for(int i=1,u,v;i<=m;i++){cin>>u>>v>>w[i]; addedge(u,v,w[i]);}
    	sort(edge+1,edge+1+m,cmp) ;
    	for(int i=1;i<=m;i++) {
    		int u=edge[i].u,v=edge[i].v;
    		int fau=fid(u),fav=fid(v);
    		if(fau==fav) continue;
    		n++ ;
    		w[n]=edge[i].w;
    		trfa[fau]=trfa[fav]=fa[fau]=fa[fav]=n;
    	}
    	for(int i=n;i>=1;i--) dep[i]=dep[trfa[i]]+1;
    	for(int i=1;i<=n;i++) stfa[i][0]=trfa[i];
    	for(int i=1;i<=19;i++)
    		for(int u=1;u<=n;u++) 
    			stfa[u][i]=stfa[stfa[u][i-1]][i-1];
    	int Q; cin>>Q;
    	while(Q--) {
    		int u,v; cin>>u>>v;
    		if(fid(u)!=fid(v)) cout<<"-1\n";
    		else cout<<w[LCA(u,v)]<<"\n";
    	}
    	return 0; 
    }
    
    
posted @ 2026-07-17 21:00  _Emu  阅读(5)  评论(0)    收藏  举报