线段树分治-学习笔记

news/2025/1/10 21:52:42/文章来源:https://www.cnblogs.com/dccy/p/18664779

线段树分治-学习笔记

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

总述

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

例题 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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/867349.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

咸鱼学习第一天

markdown以及编辑器obsidian的学习 1 创建新笔记Ctrl+n Ctrl+o (可以快速打开需要的笔记;可以加文档名+笔记标题)2 文档属性设置 三个“---”可以添加日期、别名、标签 3 最常用语言一个’-‘加一个空格是一个小圆点 几个“#”+一空格就是几级标题 链接设置①:如我要在…

BurpSuite实操之重发器功能使用

手动操作来触发单独的HTTP请求,并进行应用程序响应的工具,此功能用于根据不同的情况修改和发送相后的请求并分析,通过调整Request的参数,不断尝试,通过Response查看状态,从而节省在浏览器中操作的时间。 在渗透测试过程中,我们经常使用Repeater进行请求与响应的消息验证…

G1原理—3.G1是如何提升垃圾回收效率

大纲 1.G1为了提升GC的效率设计了哪些核心机制 2.G1中的记忆集是什么 3.G1中的位图和卡表 4.记忆集和卡表有什么关系 5.RSet记忆集是怎么更新的 6.DCQ机制的底层原理是怎样的 7.DCQS机制及GC线程对DCQ的处理提升G1垃圾回收器GC效率的黑科技 G1设计了一套TLAB机制 + 快速分配机制…

1.10日学习笔记之C++的类

类其实就是一种数据类型,和结构相似。类的成员包括两类,属性(成员变量)和行为(成员函数)。 成员函数定义的两种方法(可能有多种,觉得这两种比较常用) 1、将类的成员函数定义在类体内,如 class CPerson {public: short age;short getage(){return age;}};2、将类的成…

着火智能监测识别摄像机

着火智能监测识别摄像机利用了先进的图像处理技术和人工智能算法,能够快速、准确地检测环境中的着火点,从而及时采取必要的安全措施。其工作原理如下:首先,摄像机配备高清摄像头,能够实时捕捉并传输环境图像。这些图像通过内置的处理单元,进行高效处理和分析。其次,利用…

BurpSuite实操之测试器功能

对web应用程序进行自动化攻击。此功能有多种用途,如漏洞利用、模糊测试、进行暴力pojie等1、目标设置代理发送过来请求的目标主机及端口信息。 输入要攻击的目标及端口2、位置设置在这个模块可以设置攻击的参数,及攻击使用的类型(1)Sniper-狙击手(2)Battering ram-破城锤…

采沙船智能监测识别摄像机

采沙船智能监测识别摄像机还支持数据记录和分析功能,可以生成详尽的非法采沙统计报告和趋势分析,为河流管理决策提供科学依据。管理部门可以通过监控系统的数据分析,制定精准的禁采政策和优化河流资源管理,有效保护水域生态环境,防止非法采砂对生态系统的破坏。在实际应用…

C api简单查询需要的几步

C api查询简单需要的几步 1.准备sql语句 2.绑定变量 通过sqlstatement类bindout方法将查询出来的值与存储他们的变量绑定。 这里绑定是为了以后从结果集中提取一行时只需要一行就可以完成将提取出 来的变量赋值。 3.执行execute 在这一步将sql语句执行。在这里有几个重点 mysql…

iterm2

外观 这个github上的项目提供了很多主题 https://iterm2colorschemes.com/ 亮色主题感觉还可以的几个: OneHalfLight、BuiltinLight、BlulocoLight、Materil、ProLight、Tango Half Adapted 下面截图是基于 Tango Half Adapted用蓝色代替青色,黄色加深之后的效果(vim括号插件…

22. MDI窗口设计

一、什么是MDI窗口MDI 窗口(Multiple-Document Interface),又称多文档界面,它主要用于同时显示多个文档,每个文档显示在各自的窗口中。MDI 窗口中通常包含子菜单和窗口菜单,用于在窗口或文档之间进行切换。用 QMainWindow 建立的主界面,通常会同时建立或打开多个相互独立…

详解:订单履约系统规划

大家好,我是汤师爷~ 什么是订单履约系统? 订单履约是从消费者下单支付到收到商品的全流程管理过程,包括订单接收、订单派单、库存分配、仓储管理和物流配送等环节,核心目标是确保商品准时、准确地送达消费者手中。 通过订单履约系统,消费者可以实时了解商品的物流状态和预…

文件单独编译生成

编译设备树也最好在 source过的那个窗口中来编译错误提示如下: 同时在makefile文件修改时也要注意