Kruskal重构树学习笔记

挺简单的知识点(?)

概念

首先 Kruskal 算法是用来求最小生成树的算法之一,其思想是贪心。

而 Kruskal 重构树就是将整张图重建为二叉树。

在跑 Kruskal 的过程中我们会从小到大加入若干条边。现在我们仍然按照这个顺序。

首先新建 \(n\) 个集合,每个集合恰有一个节点,点权为 \(0\)

每一次加边会合并两个集合,我们可以新建一个点,点权为加入边的边权,同时将两个集合的根节点分别设为新建点的左儿子和右儿子。然后我们将两个集合和新建点合并成一个集合。将新建点设为根。

不难发现,在进行 \(n-1\) 轮之后我们得到了一棵恰有 \(n\) 个叶子的二叉树,同时每个非叶子节点恰好有两个儿子。这棵树就叫 Kruskal 重构树。

比如这张图

它会被重构成

性质

Kruskal 重构树有很多性质

  1. Kruskal 重构树是一棵二叉树

  2. 原图中所有节点与 Kruskal 重构树上的叶子节点一一对应

  3. 每个节点的点权是其子树所有节点(包括本身)中的最大点权(不考虑叶子结点)

  4. 两点之间所有简单路径上最大边权的最小值 \(=\) 最小生成树上两个点之间的简单路径上的最大值 \(=\) Kruskal 重构树上两点之间的LCA的权值。

Code

动态数组版
const int N=1e5;
int n,m;
int k,t,s,d;
struct node
{
    int u,v,w;
    bool operator<(const node& e) const {return w<e.w;}
}ee[N];
vector<int> e[N];
int d[N],f[N],a[N],down[N],fa[N][18];
int cnt;
int find(int x) 
{
    return f[x]==x ? f[x] : f[x]=find(f[x]);
}
int Kruskal()
{
    sort(ee+1,ee+1+m);
    for(int i=1;i<=2*n;++i)
        f[i]=i;
    cnt=n;
    for(int i=1;i<=m;++i)
    {
        int u=find(ee[i].u),v=find(ee[i].v);
        if(u!=v)
        {
            d[++cnt]=ee[i].w;
            f[u]=f[v]=f[cnt]=cnt;
            e[cnt].fush_back(u);
            e[cnt].fush_back(v);
        }
    }
    return cnt;
}

例题

货车运输

用的洛谷的网址((虽然我登不了洛谷)

LCA 与 Kruskal 重构树

基本上是模板题

Code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
const int INF=1<<30;
int n,m,k;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
struct node
{
    int u,v,w;
    bool operator<(const node& e) const {return w>e.w;}
}ee[N];
struct edge
{
    int next,to,v;
}e[N];
int d[N],f[N],a[N],deep[N],fa[N][30],p[N][30],head[N];
int cnt,tot;
bool vis[N];
inline void init(){for(int i=1;i<=n;++i)f[i]=i;}
inline int find(int x){return f[x]==x ? f[x] : f[x]=find(f[x]);}

inline void add(int x,int to,int v)
{
    e[++tot].to=to;
    e[tot].v=v;
    e[tot].next=head[x];
    head[x]=tot;
}
inline void dfs(int x,int f)
{
    vis[x]=true;
    for(int i=1;i<=20;++i)
    {
        fa[x][i]=fa[fa[x][i-1]][i-1];
        p[x][i]=min(p[x][i-1],p[fa[x][i-1]][i-1]);
    }
    for(int i=head[x];i;i=e[i].next)
    {
        int u=e[i].to;
        if(vis[u] || u==f)
        continue;
        deep[u]=deep[x]+1;
        p[u][0]=e[i].v;
        fa[u][0]=x;
        dfs(u,x);
    }
}
inline int LCA(int x,int y)
{
    if(deep[x]<deep[y]) swap(x,y);
    int t=deep[x]-deep[y];
    for(int i=0;i<=20;++i)
        if((1<<i) & t) x=fa[x][i];
    if(x==y) return x;
    for(int i=20;i>=0;--i)
    {
        if(fa[x][i]==fa[y][i]) continue;
        x=fa[x][i];y=fa[y][i];
    }
    return fa[x][0];
}
inline int solve(int x,int y)
{
    int ans=INF;
    if(deep[x]<deep[y]) swap(x,y);
    int t=deep[x]-deep[y];
    for(int i=0;i<=20;++i)
        if((1<<i) & t) ans=min(ans,p[x][i]),x=fa[x][i];
    if(x==y) return ans;
    for(int i=20;i>=0;--i)
    {
        if(fa[x][i]==fa[y][i]) continue;
        ans=min(ans,min(p[x][i],p[y][i]));
        x=fa[x][i];y=fa[y][i];
    }
    return min(ans,min(p[x][0],p[y][0]));
}
inline void Kruskal(int n,int m)
{
    cnt=0;
    init();
    sort(ee+1,ee+1+m);
    for(int i=1;i<=m;++i)
    {
        int u=find(ee[i].u),v=find(ee[i].v);
        if(u!=v)
        {
            f[u]=v;
            ++cnt;
            add(ee[i].u,ee[i].v,ee[i].w);
            add(ee[i].v,ee[i].u,ee[i].w);
        }
        if(cnt==n-1) break;
    }
}
int main()
{
    memset(p,0x3f,sizeof(p));
    n=read(),m=read();
    for(int i=1;i<=m;++i)
        ee[i].u=read(),ee[i].v=read(),ee[i].w=read();
    Kruskal(n,m);
    for(int i=1;i<=n;++i)
        if(!vis[i])
        dfs(i,i);
    k=read();
    while(k--)
    {
        int x,y;
        x=read(),y=read();
        if(find(x)!=find(y)) cout<<-1<<endl;
        else 
        {
            int lca=LCA(x,y);
            cout<<min(solve(lca,x),solve(lca,y))<<endl;
        }
    }
}

参考资料

oi-wiki

CSDN

posted @   HS_xh  阅读(170)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
init();
点击右上角即可分享
微信分享提示