Kruskal重构树

突然写起了归程,就先来复习一下

Kruskal重构树

性质

  1. 是一个小/大根堆(由建树时边权的排序方式决定)
  2.  $LCA(u,v)$的权值是原图u到v路径上最大/小边权的最小/大值(由建树时边权的排序方式决定)

建树

重构树中把原树的点权转换成为了新建节点的边权

  • 先将边权排序
  • 依次遍历每条边,若改变连接的两个节点u和v 不在一个并查集内,就新建一个结点node,该点点权为这条边的边权,找到 $u,v$ 所在并查集的根 $u_i,v_i$,连边$(node,u_i)(node,v_i)$,并更新并查集 $fa[u_i]=node,fa[v_i]=node$
void kruskal()
{
    int tot=n;
	for(int i=1;i<=n;++i) f1[i]=i;
	sork(a+1,a+1+m,cmp);
	for(int i=1;i=m;++i)
	{
		int u=find(a[i].u),v=find(a[i].v);
		if(u!=v)
		{
			w[++tot]=e[i].w;
			f1[tot]=f1[u]=f1[v]=tot;
			add(u,cnt),add(cnt,u);
			add(v,cnt),add(cnt,v);
		}
	}
}

  

建树复杂度:复杂度:$O(nlogn)$

例题

接下来我们来看一个简单的例题

  • BZOJ3732 Network

给你N个点的无向图 (1 <= N <= 15,000),记为:1…N。
图中有M条边 (1 <= M <= 30,000) ,第j条边的长度为: $d_j ( 1 < = d_j < = 1,000,000,000)$

现在有 K个询问 (1 < = K < = 20,000)。
每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

对于这个题目,我们很容易想到用树剖维护,或者说直接最小生成树+LCA进行维护,这一类的题目在NOIP模拟的时候就写了很多,最小生成树+LCA。
这里,我们使用Kruskal重构树来解决这个问题

Kruskal重构树若我们开始时将边权升序排序
则LCA(u,v)的权值代表 原图 u到v路径上最大边权的最小值
由于边权越大的结点深度越小
所以在这棵树上u到v的路径显然就是原图上u到v尽量沿着边权小的边走
而LCA(u,v)显然就是u到v路径上深度最小的结点
反之若我们一开始将边权降序排序
则LCA(u,v)的权值代表 原图 u到v路径上最小边权的最大值

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

#define ll long long
#define re register
#define gc getchar()
inline int read()
{
 	re ll x(0),f(1);re char c(gc);
    while(c>'9'||c<'0')f=c=='-'?-1:1,c=gc;
    while(c>='0'&&c<='9')x=x*10+c-48,c=gc;
    return f*x;
} 

const int N=2e5+10;
int n,m,q,cnt,h[N],f1[N],w[N],tot;
struct edge {int u,v,w;}a[N];
struct node {int next,to;}e[N];
bool cmp(edge a,edge b){return a.w<b.w;}
void add(int u,int v) {e[++cnt]=(node){h[u],v},h[u]=cnt;}
#define QXX(u) for(int i=h[u],v;v=e[i].to,i;i=e[i].next)
int find(int x) {return x==f1[x]?x:f1[x]=find(f1[x]);}
int fa[N],son[N],siz[N],dep[N],top[N];

void dfs1(int u,int f)
{
	QXX(u)
		if(v!=f)
		{
			fa[v]=u;
			siz[v]=1;
			dep[v]=dep[u]+1;
			dfs1(v,u);
			siz[u]+=siz[v];
			if(siz[v]>siz[son[u]])
				son[u]=v;
		}
}
void dfs2(int u)
{
	if(son[u])
		top[son[u]]=top[u],dfs2(son[u]);
	QXX(u)
		if(v!=fa[u]&&v!=son[u])
			top[v]=v,dfs2(v);
}

void kruskal()
{
    tot=n;
	for(int i=1;i<=n;++i) f1[i]=i;
	sort(a+1,a+1+m,cmp);
	for(int i=1;i<=m;++i)
	{
		int u=find(a[i].u),v=find(a[i].v);
		if(u!=v)
		{
			w[++tot]=a[i].w;
			f1[tot]=f1[u]=f1[v]=tot;
			add(u,tot),add(tot,u);
			add(v,tot),add(tot,v);
		}
	}
}

int lca(int u,int v)
{
	while(top[u]!=top[v])
    {
        if(dep[top[u]]>dep[top[v]]) u=fa[top[u]];
        else v=fa[top[v]];
    }
    if(dep[u]<dep[v])return u;
    else return v;
}
int main()
{
	n=read(),m=read(),q=read();
	for(int i=1;i<=n;++i)
		a[i].u=read(),a[i].v=read(),a[i].w=read();
	kruskal();
	top[tot]=tot;
	dfs1(tot,0);
	dfs2(tot);
	while(q--)
	{
		int u=read(),v=read();
		cout<<w[lca(u,v)]<<endl;
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/zijinjun/p/10805383.html