Kruskal 重构树学习笔记

Kruskal 重构树学习笔记

参考博客

链接

性质

这里给定最小生成树 Kruskal 重构树 的性质,那么最大生成树 Kruskal 重构树 的性质可以同理得。

重构树是一棵恰有 n 个叶子节点的完满二叉树,每个非叶子节点都有两个儿子共有 2n−1 个点。(初始图中的 n 个点是叶子,由于是棵树,边数为 n−1 ,相当于每条边都新建了一个点,那么点数自然是 2n−1 )。

重构树的点权符合大根堆的性质。(从上到下边权递减,最上面的点边权最大,因为边权大的边后面加)。

原图中两点间所有简单路径的最大边权最小值,等于最小生成树上两点之间边权最大值,等于重构树上两点 LCA 的点权。(这下任意两点间的最小瓶颈路就只用求一个 LCA 了)。

从 u 出发,经过边权不超过 k 的边,所能到达的点恰好是重构树上某棵子树内的所有叶子节点。(可以这么想,从 u 点一直向上走,直到点权大于 k,那么那个点相当于就不能过,由于是一颗树,不能经过一个点,断开之后 u 所在部分就是一颗子树)。

模板题

P1967 [NOIP2013 提高组] 货车运输

题目链接

按照从大到小排序,然后建图,然后树链剖分/倍增求一下LCA即可。

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

//开2倍空间
const int N=1e5+10;

struct node{
    int u,v,w;
    bool operator<(const node &u)const{
        return w>u.w;
    }
}edge[N];

int p[N];
int find(int x){
    return p[x]==x?x:p[x]=find(p[x]);
}

int n,m,q;
vector<int> e[N];
int fa[N],dep[N],son[N],sz[N];
int top[N],tot,w[N];

//预处理各个信息
void dfs1(int u,int father){
  fa[u]=father;
  dep[u]=dep[father]+1;
  sz[u]=1;
  for(auto v:e[u]){
    if(v==father) continue;
    dfs1(v,u);
    sz[u]+=sz[v];
    if(sz[son[u]]<sz[v]) son[u]=v;
  }
}

void dfs2(int u,int tp){
  top[u]=tp;
  if(!son[u]) return;
  dfs2(son[u],tp);
  for(auto v:e[u]){
    if(v==fa[u]||v==son[u]) continue;
    dfs2(v,v);
  }
}

int lca(int u,int v){
  while(top[u]!=top[v]){
    if(dep[top[u]]<dep[top[v]]) swap(u,v);
    u=fa[top[u]];
  }
  return dep[u]<dep[v]?u:v;
} 

void kruskalRT(){
    sort(edge+1,edge+m+1);
    for(int i=1;i<=2*n;i++) p[i]=i;
    tot=n;//虚点从n+1开始建
    for(int i=1;i<=m;i++){
        int u=find(edge[i].u),v=find(edge[i].v);
        if(u==v) continue;
        p[u]=p[v]=++tot;
        w[tot]=edge[i].w;
        e[tot].push_back(u);
        e[u].push_back(tot);
        e[tot].push_back(v);
        e[v].push_back(tot);
        if(tot==(n<<1|1)) break;
    }
}

void Showball(){
    cin>>n>>m;
    for(int i=1;i<=m;i++) cin>>edge[i].u>>edge[i].v>>edge[i].w;
    
    kruskalRT();

    //有可能为一个森林
    for(int i=tot;i>n;i--) if(!sz[i]) dfs1(i,0);
    for(int i=tot;i>n;i--) if(!top[i]) dfs2(i,i);

    int q;
    cin>>q;
    while(q--){
        int u,v;
        cin>>u>>v;
        if(find(u)!=find(v)) cout<<"-1\n";
        else cout<<w[lca(u,v)]<<"\n";
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}

双倍经验

三倍经验

posted @ 2024-12-14 03:36  Showball  阅读(7)  评论(0编辑  收藏  举报