zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    Vector成员为指针时要注意的问题
    (转)C++中的static关键字的总结
    (转)内联(inline)函数与虚函数(virtual)的讨论
    基类类型和派生类型之间的转换以及虚函数
    (转) vector的reserve和resize
    关于函数返回值的一些见解
    (转)感受异或的神奇
    一个mysql不解的问题
    获取本机TCP连接状态信息
    (转)socket的SYN_RECV和ESTABLISHED状态
  • 原文地址:https://www.cnblogs.com/zijinjun/p/10805383.html
Copyright © 2011-2022 走看看