「Luogu P5494 【模板】线段树分裂」
(因为没有认证,所以这道题就由Froggy上传)
线段树分裂用到的地方确实并不多,luogu上以前也没有这道模板题,所以就出了一道,实在是想不出怎么出模板了,所以这道题可能可以用一些其他的算法水过去.
前置芝士
- 线段树: 本题中用到的是权值线段树(查询每个数在序列中出现的次数,序列中第k大的数等操作).
- 线段树合并: 为了增加一下码量才放上的.
线段树分裂
既然是模板题,这个自然才是重点.
以下这样一颗线段树:
需要将橙色线段覆盖的部分分裂出来,需要建一颗新的树,当一个节点所代表的区间与需要分裂的区间有交时需要开一个新的节点(绿色部分),原来的树中需要将那些整个被分裂的部分直接接到新的树下面,并且将于原来的树的边断开(红色部分),可以发现被断开的边最多只会有\(\log_2N\)条,所以最终每次分裂的时间复杂度就是\(\log_2N\)(证明方法与区间修改为\(N\log_2N\)相同).
代码
#include<bits/stdc++.h>
#define rap(i,first,last) for(int i=first;i<=last;++i)
//线段树标准define
#define Lson (tree[now].lson)
#define Rson (tree[now].rson)
#define Middle ((left+right)>>1)
#define Left Lson,left,Middle
#define Right Rson,Middle+1,right
#define Now nowleft,nowright
using namespace std;
const int maxN=6e5+7;//因为有分裂,需要大一点的空间
int N,M;
struct Tree//线段树中每一个节点
{
int lson,rson;//动态开点,记录左右儿子
long long sum;//区间和
}tree[maxN*4];
int cnt=0;
//一下是一个空间回收,可以将在线段树合并后没有用的点回收,更好地利用空间
int tot=0;//删掉的节点的个数
int rubbish[maxN*4];//用来放删掉的节点的编号
void Del(int &now)//删除一个节点
{
tree[now].lson=tree[now].rson=tree[now].sum=0;//先清空
rubbish[++tot]=now;//放入垃圾桶
now=0;
}
int New()//得到一个新的节点
{
if(tot)return rubbish[tot--];//垃圾桶不是空的就从垃圾桶上面拿一个
return ++cnt;//垃圾桶是空的就拿一个全新的节点
}
//空间回收代码结束
int arr[maxN];//开始读入的数组
void PushUp(int now)//合并信息
{
tree[now].sum=tree[Lson].sum+tree[Rson].sum;
}
void Build(int &now,int left=1,int right=N)//建树
{
if(!now)now=New();//得到一个新节点
if(left==right)
{
tree[now].sum=arr[left];//叶节点直接赋值
return;
}
Build(Left);//建左子树
Build(Right);//建右子树
PushUp(now);//合并信息
}
void Merge(int &tree1,int &tree2,int left=1,int right=N)//线段树合并,将tree2合并到tree1上
{
if(!tree1||!tree2)//如果当前这棵树只有其中一颗树有就可以直接赋值
{
tree1+=tree2;
return;
}
if(left==right)//叶节点就直接合并
{
tree[tree1].sum+=tree[tree2].sum;
Del(tree2);//删掉tree2
return;
}
Merge(tree[tree1].lson,tree[tree2].lson,left,Middle);//继续合并
Merge(tree[tree1].rson,tree[tree2].rson,Middle+1,right);
Del(tree2);//删掉tree2
PushUp(tree1);//合并信息
}
void Split(int &tree1,int &tree2,int nowleft,int nowright,int left=1,int right=N)//线段树分裂(将tree1中nowleft~nowright部分分裂到tree2中)
{
if(right<nowleft||nowright<left)return;//没有被覆盖就直接返回
if(!tree1)return;//如果tree1没有自然也没有用了
if(nowleft<=left&&right<=nowright)//被完全覆盖时
{
tree2=tree1;
tree1=0;//把边断开
return;
}
if(!tree2)tree2=New();//得到一个新节点
Split(tree[tree1].lson,tree[tree2].lson,Now,left,Middle);//左右子树继续分裂
Split(tree[tree1].rson,tree[tree2].rson,Now,Middle+1,right);
PushUp(tree1);//合并两数信息
PushUp(tree2);
}
void UpData(int num,int add,int &now,int left=1,int right=N)//单点修改(不多讲)
{
if(num<left||num>right)return;
if(!now)now=New();
if(left==right)
{
tree[now].sum+=add;
return;
}
UpData(num,add,Left);
UpData(num,add,Right);
PushUp(now);
}
long long QuerySum(int nowleft,int nowright,int now,int left=1,int right=N)//查询区间和(不多讲)
{
if(nowright<left||right<nowleft)return 0;
if(!now)return 0;
if(nowleft<=left&&right<=nowright)
{
return tree[now].sum;
}
return QuerySum(Now,Left)+QuerySum(Now,Right);
}
int QueryKth(long long k,int now,int left=1,int right=N)//查询区间第k大(不多讲)
{
if(k<=0)return -1;
if(left==right)return left;
if(tree[Lson].sum>=k)return QueryKth(k,Left);
return QueryKth(k-tree[Lson].sum,Right);
}
int root[maxN];//记录每一个序列的线段树的根节点
int cnttree=1;//序列的编号
int main()
{
scanf("%d%d",&N,&M);
rap(i,1,N)scanf("%d",&arr[i]);
Build(root[1]);//以root[1]建树
int check,p,t,x,y,k,q,kth;
rap(i,1,M)
{
scanf("%d",&check);
if(check==0)
{
scanf("%d%d%d",&p,&x,&y);
Split(root[p],root[++cnttree]/*建一颗新树*/,x,y);//分裂
}
if(check==1)
{
scanf("%d%d",&p,&t);
Merge(root[p],root[t]);//合并
}
if(check==2)
{
scanf("%d%d%d",&p,&x,&q);
UpData(q,x,root[p]);//修改
}
if(check==3)
{
scanf("%d%d%d",&p,&x,&y);
printf("%lld\n",QuerySum(x,y,root[p]));//区间和
}
if(check==4)
{
scanf("%d%d",&p,&k);
if(QuerySum(1,N,root[p])<k)kth=-1;//区间和都没有k自然是不会有第k大了
else
kth=QueryKth(k,root[p]);
printf("%d\n",kth);
}
}
}
「Luogu P5494 【模板】线段树分裂」的更多相关文章
- 有趣的线段树模板合集(线段树,最短/长路,单调栈,线段树合并,线段树分裂,树上差分,Tarjan-LCA,势能线段树,李超线段树)
线段树分裂 以某个键值为中点将线段树分裂成左右两部分,应该类似Treap的分裂吧(我菜不会Treap).一般应用于区间排序. 方法很简单,就是把分裂之后的两棵树的重复的\(\log\)个节点新建出来, ...
- [HEOI2016/TJOI2016] 排序 解题报告(二分答案/线段树分裂合并+set)
题目链接: https://www.luogu.org/problemnew/show/P2824 题目描述: 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在 ...
- hdu 1754 I Hate It (模板线段树)
http://acm.hdu.edu.cn/showproblem.php?pid=1754 I Hate It Time Limit: 9000/3000 MS (Java/Others) M ...
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
- [BZOJ4552][TJOI2016&&HEOI2016]排序(二分答案+线段树/线段树分裂与合并)
解法一:二分答案+线段树 首先我们知道,对于一个01序列排序,用线段树维护的话可以做到单次排序复杂度仅为log级别. 这道题只有一个询问,所以离线没有意义,而一个询问让我们很自然的想到二分答案.先二分 ...
- luoguP2824 [HEOI2016/TJOI2016]排序(线段树分裂做法)
题意 所谓线段树分裂其实是本题的在线做法. 考虑如果我们有一个已经排好序的区间的权值线段树,那么就可以通过线段树上二分的方法得到第\(k\)个数是谁. 于是用set维护每个升序/降序区间的左右端点以及 ...
- BZOJ4552 HEOI2016/TJOI2016排序(线段树合并+线段树分裂)
很久以前写过二分答案离线的做法,比较好理解.事实上这还是一个线段树合并+分裂的板子题,相比离线做法以更优的复杂度做了更多的事情.具体不说了.怎么交了一遍luogu上就跑第一了啊 #include< ...
- hdu3966 树链剖分点权模板+线段树区间更新/树状数组区间更新单点查询
点权树的模板题,另外发现树状数组也是可以区间更新的.. 注意在对链进行操作时方向不要搞错 线段树版本 #include<bits/stdc++.h> using namespace std ...
- 【线段树】【P3372】模板-线段树
百度百科 Definition&Solution 线段树是一种log级别的树形结构,可以处理区间修改以及区间查询问题.期望情况下,复杂度为O(nlogn). 核心思想见百度百科,线段树即将每个 ...
随机推荐
- 设置textarea不可拉伸
默认情况下,我们将鼠标移动到textarea的右下角时发现文本域是可以通过拖动的方式改变其大小的,这会影响我们原本的页面布局.若想设置其不可拖动,可为其添加如下属性: style="resi ...
- Redis-消息
Redis 发布订阅(实际开发不使用) Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息. Redis 客户端可以订阅任意数量的频道. 下图 ...
- 放眼全球,关注游戏质量变化:腾讯WeTest发布《2019中国移动游戏质量白皮书》
2019是中国游戏市场,尤其是手游市场称得上是跌宕起伏的一年,同时也是各大厂商推陈出新突破过去的一年.面对竞争激烈的市场,手游厂商们不仅着眼于游戏质量的提升,更是将一众优秀的国产游戏带入到了海外市场, ...
- 笔记本电脑插上耳机声音依然外放解决方法 为什么插入HDMI线,电脑的音响就没有声音了
笔记本电脑插上耳机声音依然外放解决方法: 下载一个驱动大师,安装声卡驱动.(驱动人生安装的驱动有可能不能用) 为什么插入HDMI线,电脑的音响就没有声音了 参考:https://zhidao.baid ...
- 【前端之BOM和DOM】
" 目录 #. window对象介绍 #. window子对象 1. 浏览器对象 navigator 2. 屏幕对象 screen 3. 历史 history 4. 地址(URL) loc ...
- Tarjan's algorithm
Tarjan算法可以用来求有向图的强连通分量个数,之前十分粗略的写了Kosaraju算法,这里打算比较认真的分析一下Tarjan算法,然后给出算法实现代码. Tarjan算法的主要算法部分也是dfs( ...
- enviroment linux jdk and git and maven
#java_home export JAVA_HOME=/usr/local/java/jdk1.8.0_211 export JRE_HOME=$JAVA_HOME/jre export CLASS ...
- 三分钟让你秒懂.Net生态系统
提到.Net的时候,大多数人的第一反应可能就是.Net Framework和Visual Studio..Net Framework的第一个版本发布与2002年2月13日,这对于科技发展日新月异的时代 ...
- JavaScript动画相关
JS动画原理 通过CSS缓慢变化从而实现动画效果 获取css属性 Window.getComputedStyle()方法返回一个对象,该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素 ...
- 关于 UIDatePicker 在iOS9 系统上的一个坑
在使用 UIDatePicker时,在iOS9系统上上遇到一个很奇怪的问题,在其他系统版本中没发现,设置年月日格式显示的视图,在iOS9设备上出现中间月份无法显示的问题: 检查代码没问题,这个视图是使 ...