首页 > 其他分享 >Kruskal 重构树学习笔记

Kruskal 重构树学习笔记

时间:2024-07-26 14:08:05浏览次数:9  
标签:重构 ch int Kruskal 笔记 st &&

Kruskal 想必大家都不陌生,这是一种求最小生成树的算法。
关于 Kruskal 重构树,就是把一张图转化为一个堆。
具体来说,我们可以处理出来从 \(u\) 到 \(v\) 路径中的点权或边权的极值。
image
比如上面这张图(前为编号,[ ]内为点权),我们可以将它重构为小顶堆,如下
image
请注意,这棵树有着严格的方向,节点11为他的根节点。
那么如何去实现这个过程呢。
我们仍然是将边排序,然后扫描,用并查集判断是否合法,和正常的 \(Kruskal\) 最小生成树算法一样。
如果当前边 \((u,v)\) 合法,我们需要找到 \(u\) 的父亲 \(fu\) 和 \(v\) 的父亲 \(fv\),然后新建一个节点 \(tot\) 点权为 \(w_tot=min(w_fu,w_fv)\)。新建两条边 \(tot\to fu\) 和 \(tot\to fv\),当合法的边数达到 \(2\cdot n-2\) 条时,算法结束。
显然最后会构成一颗二叉树,且满足 \(w_i\le w_x(i\in siz_x)\)。这棵树的所有叶子结点都是真实点,且任意两点之间路径上的最小节点权值为这两个点的 \(lca\) 的权值。
那么 \(Kruskal\) 重构树能够解决什么样的问题呢?
P1967 [NOIP2013 提高组] 货车运输
这道题要求两点之间路线中边权的最小值最大,我们可以重构出一个小顶堆,然后查询 \(u\) 和 \(v\) 的 \(lca\) 的权值是否合法即可。
求 \(lca\) 直接倍增即可。
代码如下

#include<bits/stdc++.h>
#define endl '\n'
inline int read(){
    char ch=getchar();int x=0,f=1;
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
    return x*f;
}
const int N=1e5+10;
int n,m,q,fa[N],st[20][N],head[N],cnt,dfn_cnt,dfn[N],f[N],dep[N],w[N];
bool vis[N];
struct EDGE{int u,v,w;}ed[N];
struct edge{int v,nex;}e[N<<1];
inline void add(int u,int v){e[++cnt]={v,head[u]},head[u]=cnt;}
inline bool cmp(EDGE a,EDGE b){return a.w>b.w;}
inline int get(int x,int y){return dep[x]<dep[y]?x:y;}
inline void dfs(int x,int fa){
    vis[x]=1;
    st[0][dfn[x]=++dfn_cnt]=x;f[x]=fa;dep[x]=dep[fa]+1;
    for(int i=head[x];i;i=e[i].nex)dfs(e[i].v,x);
}
inline int LCA(int u,int v){
    if(u==v)return u;
    if((u=dfn[u])>(v=dfn[v]))std::swap(u,v);
    int d=std::__lg(v-u++);   
    return f[get(st[d][u],st[d][v-(1<<d)+1])];
}
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void Kruskal(){
    for(int i=1;i<=2*n;++i)fa[i]=i;
    std::stable_sort(ed+1,ed+m+1,cmp);
    int _new=n;
    for(int i=1;i<=m&&cnt<=2*n-2;++i){
        int u=find(ed[i].u),v=find(ed[i].v);
        if(u==v)continue;
        ++_new;add(_new,u),add(_new,v);
        fa[u]=fa[v]=_new;
        w[_new]=ed[i].w;
    }
    for(int i=_new;i;--i)
        if(!vis[i])dfs(i,0);
    n*=2;
    for(int i=1;i<=std::__lg(n);++i)
        for(int j=1;j+(1<<i)-1<=n;++j)
            st[i][j]=get(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
inline void work(int u,int v){
    if(find(u)!=find(v)){std::cout<<-1<<'\n';return;}
    std::cout<<w[LCA(u,v)]<<'\n';
}
int main(){
    // freopen("in.in","r",stdin),freopen("out.out","w",stdout);
    std::ios::sync_with_stdio(false);
    std::cin.tie(0),std::cout.tie(0);
    n=read(),m=read();
    for(int i=1;i<=m;++i)ed[i].u=read(),ed[i].v=read(),ed[i].w=read();
    Kruskal();
    q=read();
    for(int i=1,u,v;i<=q;++i)u=read(),v=read(),work(u,v);
}

其中 Kruskal 重构树(板子)的部分是

inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void Kruskal(){
    for(int i=1;i<=2*n;++i)fa[i]=i;
    std::stable_sort(ed+1,ed+m+1,cmp);
    int _new=n;
    for(int i=1;i<=m&&cnt<=2*n-2;++i){
        int u=find(ed[i].u),v=find(ed[i].v);
        if(u==v)continue;
        ++_new;add(_new,u),add(_new,v);
        fa[u]=fa[v]=_new;
        w[_new]=ed[i].w;
    }
    for(int i=_new;i;--i)
        if(!vis[i])dfs(i,0);
    n*=2;
    for(int i=1;i<=std::__lg(n);++i)
        for(int j=1;j+(1<<i)-1<=n;++j)
            st[i][j]=get(st[i-1][j],st[i-1][j+(1<<i-1)]);
}

这是另一道题 P4899 [IOI2018] werewolf 狼人
思路:从起点找到所有可以到达的点权大于等于 \(L\) 的节点,为集合 \(A\),然后从终点找到所有可以到达的点权小于等于 \(R\) 的点,为集合 \(B\)。如果 \(A\bigcap B\ne \varnothing\),则存在这样的路径,否则不存在。
考虑 kruskal 重构树,构建一个小顶堆(人走),然后从起点往上跳到最后一个权值大于等于 \(L\) 的节点,此时这个点的子树中就全是权值小于等于 \(L\) 的点。大顶堆(furry走)同理。
两个堆分别建一个 dfn 序列 \(a\) 和 \(b\),此时就相当于询问序列 \(a\) 中 \(l_a\) 到 \(r_a\) 范围内的元素有没有在序列 \(b\) 中 \(l_b\) 到 \(r_b\) 的范围内出现。(二维数点),直接上离线树状数组或者主席树即可。

#include<bits/stdc++.h>
inline int read(){
	char ch=getchar();int x=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
const int N=4e5+10;
int n,m,q;
struct Edge{int u,v,w;}ed[N];
inline bool cmp_per(Edge a,Edge b){return a.w>b.w;}
inline bool cmp_wolf(Edge a,Edge b){return a.w<b.w;}
struct Ktree{
	int dfn[N],fa[N],st[25][N],head[N],val[N],dfn_cnt=0,e_cnt=0,lf[N],rf[N];
	struct edge{int v,nex;}e[N];
	inline void add(int u,int v){e[++e_cnt]={v,head[u]};head[u]=e_cnt;}
	inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
	inline void dfs(int x){
		lf[x]=++dfn_cnt;dfn[dfn_cnt]=x;
		for(int i=1;i<=20;++i)st[i][x]=st[i-1][st[i-1][x]];
		for(int i=head[x];i;i=e[i].nex)dfs(e[i].v);
		rf[x]=dfn_cnt;
	}
	inline void build(int type){
		for(int i=1;i<=2*n-1;++i)fa[i]=i;
		for(int i=1;i<=m;++i)ed[i].w=type?std::min(ed[i].u,ed[i].v):std::max(ed[i].u,ed[i].v);
		if(type)std::stable_sort(ed+1,ed+m+1,cmp_per);else std::stable_sort(ed+1,ed+m+1,cmp_wolf);
		int node_tot=n+1;
		for(int i=1;i<=m&&node_tot<=2*n-1;++i){
			int u=ed[i].u,v=ed[i].v,w=ed[i].w;
			int fu=find(u),fv=find(v);
			if(fu!=fv){
				val[node_tot]=w;
				add(node_tot,fu),add(node_tot,fv);
				st[0][fu]=st[0][fv]=fa[fu]=fa[fv]=node_tot;
				node_tot++;
			}
		}
		dfs(node_tot-1);
	}
	inline int get(int x,int v,int type){
		for(int i=20;i>=0;--i){
			if(type&&st[i][x]&&val[st[i][x]]>=v)x=st[i][x];
			if(!type&&st[i][x]&&val[st[i][x]]<=v)x=st[i][x];
		}
		return x;
	}
}Per,Wolf;
int root[N],cnt;
struct Tree{int siz,ls,rs;}t[N<<5];
inline void update(int p){t[p].siz=t[t[p].ls].siz+t[t[p].rs].siz;}
inline void insert(int last,int p,int x,int l,int r){
	if(l==r){t[p].siz=t[last].siz+1;return;}
	int mid=(l+r)>>1;t[p]=t[last];
	if(x<=mid)insert(t[last].ls,t[p].ls=++cnt,x,l,mid);
	else insert(t[last].rs,t[p].rs=++cnt,x,mid+1,r);
	update(p);
}
inline int query(int last,int p,int x,int y,int l,int r){
	if(l>=x&&r<=y){return t[p].siz-t[last].siz;}
	int mid=(l+r)>>1,ans=0;
	if(x<=mid)ans+=query(t[last].ls,t[p].ls,x,y,l,mid);
	if(y>mid)ans+=query(t[last].rs,t[p].rs,x,y,mid+1,r);
	return ans;
}
int main(){
	// freopen("in.in","r",stdin),freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0),std::cout.tie(0);
	n=read(),m=read(),q=read();
	int len=2*n-1;
	for(int i=1;i<=m;++i)
		ed[i].u=read()+1,ed[i].v=read()+1;
	Per.build(1),Wolf.build(0);
	for(int i=1;i<=Wolf.dfn_cnt;++i){
		root[i]=root[i-1];
		int x=Wolf.dfn[i];
		if(x<=n)insert(root[i-1],root[i]=++cnt,Per.lf[x],1,len);
	}
	for(int i=1;i<=q;++i){
		int u=read(),v=read(),l=read(),r=read();
		u++,v++,l++,r++;
		int start=Per.get(u,l,1),end=Wolf.get(v,r,0);
		if(query(root[Wolf.lf[end]-1],root[Wolf.rf[end]],Per.lf[start],Per.rf[start],1,len))
			std::cout<<1<<'\n';
		else std::cout<<0<<'\n';
	}
}

标签:重构,ch,int,Kruskal,笔记,st,&&
From: https://www.cnblogs.com/Ishar-zdl/p/17982471

相关文章

  • 不依赖本地系统调度jupyter笔记本
    是否可以在不依赖本地系统的情况下调度使用一个驱动器文件的jupyter笔记本。我在公司的共享点中创建了一个驱动器的快捷方式,并在我的jupyter笔记本中读取这些文件(笔记本从onedrive读取csv文件)。我目前每天在jupyterlab中安排笔记本,但这是在我的本地系统中,需要每天打开我......
  • C++自学笔记17(const和mutable)
    const在之前的笔记中我们出现很多次constchar*name=“shaojie”,定义一个不可变指针存放字符串。不可变就来自const,表示“只读、常量”为什么需要它呢?我们需要一些东西不可被修改。const加数据变量#include<iostream>intmain(){constintMAX_AGE=99;M......
  • C++自学笔记18(成员初始化列表和初始化对象)
    成员列表初始化创建变量,并将其初始化是创建函数的必要部分。#include<iostream>#include<string>classEntity{private:std::stringm_name;public:Entity(){m_name="nothing"}Entity(conststd::string&name){......
  • 《左耳听风 传奇程序员练级攻略》读书笔记
    本书是程序员大牛陈皓的文章汇总,内容包括技术、沟通、工程师文化等,通读之后摘录其中精华部分。开卷有益,能读到摘录部分也会收益,当然最好是去读原文,知识转化效率更高。除本书之外,还有一些他的文章也非常值得阅读,包括程序员如何变现,如何学习英语主题等。05有竞争力的程序员好的......
  • React18学习笔记 第六篇:对React内部运作深入了解
    Part1组件之间的概念差异·组件组件是我们为了描述用户界面的一部分而编写的,它只是一个普通的JavaScript函数,但它是一个返回React式元素的函数,这些元素是用JSX语法来编写的,我们可以把组件理解为一个“蓝图”或“模板”。·组件实例一个组件实例就像是一个组件的实际物理表......
  • 计组笔记第六章——总线
    6.1.1总线概述总线简图:总线的物理实现:“一根”;数据总线可能包含多跟信号线,所有硬件部件都可以通过这跟总线传递数据。同一时刻只能有一个部件发送数据,但可以有多个部件接受数据。基本概念总线的定义:总线是一组能为多个部件分时共享的公共信息传送线路。为什么要用总线......
  • NodeJs 学习笔记
    Node.js是一个基于ChromeV8JavaScript引擎的开源运行环境,用于开发服务器端和网络应用。Node.js允许开发者使用JavaScript编写命令行工具和服务器端的应用程序,并且可以无缝地在从服务器到桌面应用再到移动设备的各种环境中运行。Node.js的核心原理包括:事件驱动:Node.......
  • 整理的比较全面的C语言入门笔记!
    c语音在线教程:54笨鸟C语言一经出现就以其功能丰富、表达能力强、灵活方便、应用面广等特点迅速在全世界普及和推广。C语言不但执行效率高而且可移植性好,可以用来开发应用软件、驱动、操作系统等。C语言也是其它众多高级语言的鼻祖语言,所以说学习C语言是进入编程世界的必修课。......
  • Manim 学习笔记(二)--文本测试
    文本测试--效果:代码:#-*-coding:utf-8-*-frommanimimport*classTransformExample(Scene):defconstruct(self):banner=ManimBanner()banner.shift(UP*0.5)self.play(banner.create(),run_time=1)self.play(banner.anima......
  • Manim 学习笔记(三)--坐标系与坐标平面
    坐标系与坐标平面--效果:代码:#-*-coding:utf-8-*-frommanimimport*classZBX_ZBPM(Scene):defconstruct(self):#坐标平面(网格)my_plane=NumberPlane(faded_line_ratio=2,x_range=[-8,8,1],#[前两个参数的......