• Kruskal重构树


    用途

    (Kruskal)重构树可以维护树上两个点联通的最长边的最小值。

    实现

    首先我们要知道一个叫最小瓶颈生成树的东西,它和最小生成树的不同的地方是最小生成树是要求权值总和最小而最小瓶颈生成树是要求最大边最小。回顾(Kruskal)算法,我们可以发现用这种方法生成的最小生成树一定是最小瓶颈生成树。

    对于(Kruskal)算法过程,我们发现如果当前边连接两个联通块(A)(B),那么一定是(A)(B)中的点互相联通的最长边的最小值。

    有了以上结论我们发现我们可以将这个边值新建一个有权值的节点,这样如果以后要查询两个点联通的最长边的最小值只需要查它们的(LCA)的权值即可。

    题目

    Network

    题目链接

    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int N=2e5+100,M=3e5+100;
    struct edge{
    	int s,e,v,net;
    }ed[N<<1],id[M];
    int n,m,tot,k;
    int head[N<<1],size[N<<1],deep[N<<1],top[N<<1],father[N<<1],son[N<<1],f[N<<1],val[N<<1];
    inline bool cmp(edge a,edge b) {return a.v<b.v;}
    inline int getf(int x) {return f[x]==x ? x:f[x]=getf(f[x]);}
    inline int LCA(int x,int y)
    {
    	while (top[x]!=top[y])
    	{
    		if (deep[top[x]]>deep[top[y]]) swap(x,y);
    		y=father[top[y]];
    	}
    	return deep[x]<deep[y] ? x:y;
    }
    inline void dfs2(int x,int tp)
    {
    	top[x]=tp;
    	if (son[x]) dfs2(son[x],tp);
    	for (int i=head[x];i;i=ed[i].net)
    	if (ed[i].e!=father[x]&&ed[i].e!=son[x])
    	dfs2(ed[i].e,ed[i].e);
    	return ;
    }
    inline void dfs1(int x,int fa)
    {
    	deep[x]=deep[fa]+1;
    	father[x]=fa;
    	size[x]=1;
    	for (int i=head[x];i;i=ed[i].net)
    	if (ed[i].e!=fa)
    	{
    		dfs1(ed[i].e,x);
    		size[x]+=size[ed[i].e];
    		if (size[son[x]]<size[ed[i].e])
    		son[x]=ed[i].e;
    	}
    	return ;
    }
    inline void add(int s,int e)
    {
    	ed[++tot]=(edge){s,e,0,head[s]};
    	head[s]=tot;
    	return ;
    }
    int main()
    {
    	scanf("%d%d%d",&n,&m,&k);
    	for (int i=1;i<=m;i++)
    	{
    		int s,e,v;
    		scanf("%d%d%d",&s,&e,&v);
    		id[i]=(edge){s,e,v,0};
    	}
    	sort(id+1,id+m+1,cmp);
    	for (int i=1;i<=n*2;i++)
    	f[i]=i;
    	int num=0,cnt=n;
    	for (int i=1;i<=m;i++)
    	{
    		int a=getf(id[i].s),b=getf(id[i].e);
    		if (a!=b)
    		{
    			f[a]=f[b]=++cnt;
    			add(cnt,a);add(cnt,b);
    			val[cnt]=id[i].v;
    			num++;
    			if (num==n-1) break;
    		}
    	}
    	dfs1(cnt,0);
    	dfs2(cnt,cnt);
    	while (k--)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		printf("%d
    ",val[LCA(x,y)]);
    	}
    	return 0;
    }
    

    P4768 [NOI2018]归程

    题目链接

    注意事项

    • (Kruskal) 重构树是对边建立一个新的点,所以可以说是将边权转变为点权
    • (Kruskal) 重构树因为新建了节点,所以节点数要乘 (2) ,并查集也要初始化两倍,并查集合并的父亲为这两个点重构的新点
  • 相关阅读:
    DataSet中的数据全部插入数据库
    SQL养成一个好习惯是一笔财富
    C#不管什么四舍五入,只要是小数取整就得加1
    XMLNode与XmlNodeList
    ASP.NET2.0中配置文件的加密与解密
    编写一个文件目录常用操作的类
    上传文件的方法
    使用javascript 实现.net 验证控件功能
    SQLSERVER如何获取一个数据库中的所有表的名称、一个表中所有字段的名称
    Android之开启内置闹钟与已安装的应用程序设置
  • 原文地址:https://www.cnblogs.com/last-diary/p/11451873.html
Copyright © 2020-2023  润新知