首页 > 其他分享 >线段树分治-学习笔记

线段树分治-学习笔记

时间:2025-01-10 21:54:41浏览次数:1  
标签:return int 线段 分治 笔记 fa getfa

线段树分治-学习笔记

阅前须知:本文给出了线段树分治的一道例题以及多道习题,同时给出了部分实现的代码,帮助学习线段树分治。

总述

线段树分治是一种离线算法,在于把修改挂在线段树的节点上,通过遍历线段树求出每个叶子节点的答案,以减小复杂度。

例题 P5787 二分图

题目大意:\(n\) 个点的图上,有 \(m\) 条边,第 \(i\) 条边 \((x,y)\) 在时刻 \(l_i\sim r_i\) 存在,问每个时刻的图是否是二分图。

解题思路:首先如果是动态加边,没有删边,怎么判断一个图是否是二分图?

可以考虑种类并查集,一共两类,一条边连接不同种类,如果连边时发现两个点属于同一种类,那么就不是二分图。这样我们就能做到 \(O(mk\alpha(n))\)。

考虑分治思想,我们把不同的询问取出它们的共同的修改,这样就能减小复杂度。

考虑把时刻 \(l_i\sim r_i\) 的修改挂在线段树上的 \(\log k\) 个区间上,这样一共有 \(m\log k\) 个修改,是可接受的。接下来我们从树根开始走,走到一个点时把修改加上,离开这个子树时把修改撤回,走到叶子时就可以记录答案了。

我们可以使用启发式合并的并查集,这样在 getfa 时就不用路径压缩,然后合并时用 vector 记录 fasz 的每个修改的位置与原来的值,在回溯时把这些修改撤销即可。

时间复杂度 \(O(k\log k\log n)\)。

参考实现:

int fa[N],sz[N];
int getfa(int x) {
	if(fa[x]==x) return x;
	return getfa(fa[x]);
}
#define mid ((l+r)>>1)
#define ls (x<<1)
#define rs (ls|1)
vp up[N];
void add(int x,int l,int r,int L,int R,int a,int b) {
	if(R<l||r<L)return;
	if(L<=l&&r<=R) {
		up[x].pb({a,b});
		return;
	}
	add(ls,l,mid,L,R,a,b),add(rs,mid+1,r,L,R,a,b);
}
void solve(int x,int l,int r,int flag) {
	vp o1,o2;
	if(!flag)for(auto [a,b]:up[x]) {
		int u=getfa(a),v=getfa(b);
		if(u==v) flag=1;
		u=getfa(a),v=getfa(b+n);
		if(u!=v) {
			if(sz[u]<sz[v]) swap(u,v);
			o1.pb({u,sz[u]}),o2.pb({v,fa[v]});
			sz[u]+=sz[v],fa[v]=u;
		}
		u=getfa(a+n),v=getfa(b);
		if(u!=v) {
			if(sz[u]<sz[v]) swap(u,v);
			o1.pb({u,sz[u]}),o2.pb({v,fa[v]});
			sz[u]+=sz[v],fa[v]=u;
		}
	}
	if(l==r) {
		if(flag) write("No\n");
		else write("Yes\n");
		for(auto [a,b]:o1) sz[a]=b;
		for(auto [a,b]:o2) fa[a]=b;
		return;
	}
	solve(ls,l,mid,flag);
	solve(rs,mid+1,r,flag);
	reverse(o1.begin(),o1.end());
	reverse(o2.begin(),o2.end());
	for(auto [a,b]:o1) sz[a]=b;
	for(auto [a,b]:o2) fa[a]=b;
}
signed main(){
	read(n,m,K);
	fo(i,1,n*2) fa[i]=i,sz[i]=1;
	fo(i,1,m) {
		int l,r,x,y; 
		read(x,y,l,r);
		++l,++r;
		if(l==r) continue; 
		add(1,1,K,l,r-1,x,y);
	}
	solve(1,1,K,0);
	return 0;
}

练习A AHOI2013 连通图

题目大意:每次询问断掉若干条边,问图是否还连通。

解题思路:我们把每条边断开的位置找出来,这样就分成了若干个区间,每个区间都表示这条边连上。

考虑线段树分治,把区间挂在线段树上。那么考虑怎么判断是否连通,用并查集缩点,如果成功缩点连通块个数减一,判断连通块个数是否为 1 即可。

参考实现:

const int N=1e6+5;
int X[N],Y[N];
vi d[N];
int fa[N],sz[N];
int getfa(int x) {
	if(fa[x]==x)return x;
	return getfa(fa[x]);
}
int n,m,K;
#define ls (x<<1)
#define rs (ls|1)
#define mid ((l+r)>>1)
vp c[N];
void add(int x,int l,int r,int L,int R,int a,int b) {
	if(R<l||r<L) return;
	if(L<=l&&r<=R) {
		c[x].pb({a,b});
		return;
	} 
	add(ls,l,mid,L,R,a,b),add(rs,mid+1,r,L,R,a,b);
}
void solve(int x,int l,int r,int s) {
	vp o1,o2;
	for(auto [a,b]:c[x]) {
		a=getfa(a),b=getfa(b);
		if(a!=b) {
			if(sz[a]<sz[b]) swap(a,b);
			o1.pb({a,sz[a]}),o2.pb({b,fa[b]});
			fa[b]=a,sz[a]+=sz[b];
			--s;
		}
	}
	if(l==r) {
		if(s==1) write("Connected\n");
		else write("Disconnected\n");
		reverse(o1.begin(),o1.end());
		reverse(o2.begin(),o2.end());
		for(auto [a,b]:o1) sz[a]=b;
		for(auto [a,b]:o2) fa[a]=b;
		return;
	}
	solve(ls,l,mid,s);
	solve(rs,mid+1,r,s);
	reverse(o1.begin(),o1.end());
	reverse(o2.begin(),o2.end());
	for(auto [a,b]:o1) sz[a]=b;
	for(auto [a,b]:o2) fa[a]=b;
}
signed main(){
	read(n,m);
	fo(i,1,m) read(X[i],Y[i]);
	read(K);
	fo(i,1,K) {
		int t; read(t);
		fo(j,1,t) {
			int x; read(x);
			d[x].pb(i);
		}
	}
	fo(i,1,m) {
		int lst=0;
		for(auto j:d[i]) {
			if(lst+1<j) add(1,1,K,lst+1,j-1,X[i],Y[i]);
			lst=j;
		}
		if(lst<K) add(1,1,K,lst+1,K,X[i],Y[i]);
	}
	fo(i,1,n) sz[i]=1,fa[i]=i;
	solve(1,1,K,n);
	return 0;
}

练习B CF813F

记录每一种边上一次出现的位置即可转化为例题。

参考实现:

const int N=1e6+5;
int fa[N],sz[N];
int getfa(int x) {
	if(fa[x]==x) return x;
	return getfa(fa[x]);
}
int tot;
map<pi,int> mp;
int X[N],Y[N];
int lst[N],opt[N];
vp d[N];
#define ls (x<<1)
#define rs (ls|1)
#define mid ((l+r)>>1)
void add(int x,int l,int r,int L,int R,int a,int b) {
	if(L<=l&&r<=R) {
		d[x].pb({a,b});
		return;
	}
	if(R<l||r<L) return;
	add(ls,l,mid,L,R,a,b),add(rs,mid+1,r,L,R,a,b);
}
void solve(int x,int l,int r,int flag){
	vp o1,o2;
	for(auto [a,b]:d[x]) {
		int u=getfa(a),v=getfa(b);
		if(u==v) flag=1;
		v=getfa(b+n);
		if(u!=v) {
			if(sz[u]<sz[v]) swap(u,v);
			o1.pb({u,sz[u]}),o2.pb({v,fa[v]});
			sz[u]+=sz[v],fa[v]=u;
		}
		u=getfa(a+n),v=getfa(b);
		if(u!=v) {
			if(sz[u]<sz[v]) swap(u,v);
			o1.pb({u,sz[u]}),o2.pb({v,fa[v]});
			sz[u]+=sz[v],fa[v]=u;
		}
	}
	if(l==r) {
		if(flag) write("NO\n");
		else write("YES\n");
		reverse(o1.begin(),o1.end());
		reverse(o2.begin(),o2.end());
		for(auto [a,b]:o1) sz[a]=b;
		for(auto [a,b]:o2) fa[a]=b;
		return;
	}
	solve(ls,l,mid,flag);
	solve(rs,mid+1,r,flag);
	reverse(o1.begin(),o1.end());
	reverse(o2.begin(),o2.end());
	for(auto [a,b]:o1) sz[a]=b;
	for(auto [a,b]:o2) fa[a]=b;
}
signed main(){
	read(n,Q);
	fo(i,1,2*n) fa[i]=i,sz[i]=1;
	fo(i,1,Q) {
		int x,y; read(x,y);
		if(mp.find({x,y})==mp.end())mp[{x,y}]=++tot,X[tot]=x,Y[tot]=y;
		int t=mp[{x,y}];
		opt[t]^=1;
		if(opt[t]==0) {
			add(1,1,Q,lst[t],i-1,x,y);
		}
		lst[t]=i;
	}
	fo(i,1,tot) if(opt[i]) add(1,1,Q,lst[i],Q,X[i],Y[i]);
	solve(1,1,Q,0);
}

标签:return,int,线段,分治,笔记,fa,getfa
From: https://www.cnblogs.com/dccy/p/18664779

相关文章

  • 1.10日学习笔记之C++的类
    ·类其实就是一种数据类型,和结构相似。类的成员包括两类,属性(成员变量)和行为(成员函数)。·成员函数定义的两种方法(可能有多种,觉得这两种比较常用)1、将类的成员函数定义在类体内,如classCPerson{public:shortage;shortgetage(){returnage;}};2、将......
  • Python基础课笔记
    文章目录第二节——Python基础课陈述句整型字符串列表字典(哈希表)判断缩进判断空值循环for循环:foriin范围字典中循环while循环列表切片删除插入循环后置函数加减乘除定义函数类(类比JAVA)定义实例化继承(类比JAVA)矩阵numpy定义合并切片正常切跳着切张量Tensor定义......
  • Border 理论学习笔记
    Border理论学习笔记约定字符串下标从\(1\)开始。定义\(S+T\)为\(S\)与\(T\)这两个字符串的拼接。定义\(S[l:r]=S_l+S_{l+1}+\cdots+S_r\)。定义\(pre(i,S)=S[i:n],suf(i,S)=S[|S|-i+1:n]\),也就是\(S\)​的前后缀。对于\(S,T\),若\(T[i:i+|S|-1]\),......
  • Qt C++学习笔记1.7
    1.7Qt入门:实现一个图片查看软件需要用到的控件:QLabelQLineEditQPushButton需要实现的功能:打开目录选择图片显示图片的名字显示图片QLabel基本用法设置文本voidsetText(constQString&);获取文本QStringtext()const;设置图片voidsetPixmap(constQPixm......
  • 1.9日学习笔记之高阻态和开漏输出
    三态:高电平、低电平和高阻态高阻态输出(High-ZOutput):高阻态输出是指一个IO口处于高阻抗状态,此时IO口既不输出高电平也不输出低电平,而是呈现高阻抗状态,相当于断开电路。高阻态输出的主要用途是:多设备共享总线:允许多个设备共享同一根数据线,但每次只有一个设备能够控制这条数据......
  • 深度学习笔记11-优化器对比实验(Tensorflow)
    ......
  • 基于扩展DDPG算法的无人机辅助无 线供电物联网网络多目标优化——学习笔记
    Ⅰ、论文笔记一、研究背景与相关工作(一)研究背景物联网技术发展促使设备数量剧增,对通信系统的数据速率和覆盖率要求提升,且设备能量供应面临挑战。5G、6G及相关技术如WPT为解决这些问题提供了支撑,无人机在无线网络中的应用也日益受到关注,其与WPT结合成为物联网网络关......
  • 《程序员修炼之道:从小工到专家》读书笔记(八)
    这篇读书笔记主要为第七章“在项目开始之前”的内容。这一章通过五个小节,详细阐述了在项目正式开始之前,程序员和团队需要面对和解决的一系列关键问题。“需求之坑”这一节让我意识到,需求的不明确或频繁变动是软件开发中常见的问题。它提醒我们,在项目启动之初,就必须投入足够的时间......
  • 笔记 配图
    柱塞泵泵头斜盘柱塞活塞冲程行程+变频器工频电机+变量恒压变流恒流变压饱和蒸汽压沸点压力气相液相气化液化亨利定律理想气体定律伺服驱动器脉冲控制PNPNPN控制器驱动器电机编码器三极管PNPNPN射极Emitter基极Base集电极Collector公共端......
  • 【数据库】全国计算机等级考试计算机三级数据库填空题真题笔记
    1.数据库分析与设计IDEF0需求建模方法由箭头和矩形框两种元素构成。矩形框代表功能活动,写在矩形框内的动词短语描述功能活动的名称。在IDEF1X建模方法中,用矩形框表示独立实体集,用圆角矩形框表示从属实体集。IDEF1X数据建模方法中,如果一个实体集的每个实例都能被唯一地标识而不......