树套树Day1线段树套平衡树bzoj3196
您需要写一种数据结构,来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)
应xgy的邀来码树套树了...今天或许能码完这一篇吧...还在发烧,动态区间第k大(权值线段树套线段树or树状数组套主席树)估计码不完了
所以正好分成几天来写,写的细一点
这种题一般很明显...就是又有平衡树性质又有线段树性质应该就是线段树套平衡树了吧
顾名思义,线段树套平衡树,就是对于线段树的每一个点开一个平衡树,利用平衡树的灵活性和线段树对区间处理的强大来解决问题
简单的来说,原来线段树每个点是一个区间,你用平衡树维护每个区间,最后的得到的就是线段树套平衡树
怎么样,理论非常简单吧 然而写起来难的一匹我会说?
来看一下这道题
OPT1:线段树常规查询区间,每一次统计小于k的点的个数再相加。
OPT2:这个是最麻烦也是最精妙的一问,解决方法是二分答案,每二分到一个答案查询一下这个答案在这个区间内的排名,如果排名等于k+1的话返回它的pre即可。注意这里二分满足条件之后不用查询pre,答案直接为left-1,可以证明left-1一定在序列中。
OPT3:相当于线段树的点修改,在平衡树中删除再插入即可。
OPT4:线段树常规查询区间,每一次找区间内比k小的最大的数,然后取max
OPT5:类似于OPT4,每一次找区间内比k大的最小的数,然后取min
大概就是这样了吧- -#
懒得写SBT,拿splay卡时限过的
大家写SBT或者Treap都是极好的,千万不要学我
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int maxn=4e6+;
const int inf=;
int ans;
int a[maxn];
int n,m;
struct Seg_Tao_Splay//原代码是分开写的...但是太丑了
{
int fa[maxn],size[maxn],son[maxn][],key[maxn],rt[maxn],cnt[maxn],Size;
inline int gt(int x){return son[fa[x]][]==x;}
inline void pushup(int x){size[x]=cnt[x]+size[son[x][]]+size[son[x][]];}
inline void sclear(int x){fa[x]=son[x][]=son[x][]=size[x]=cnt[x]=key[x]=;}
inline void rotate(int x)
{
int f1=fa[x],f2=fa[f1],wt=gt(x);
son[f1][wt]=son[x][wt^];
fa[son[f1][wt]]=f1;
son[x][wt^]=f1;
fa[f1]=x;
if (f2) son[f2][son[f2][]==f1]=x;
fa[x]=f2;
pushup(f1);
pushup(x);
}
inline void Splay(int x)
{
for(int faf;faf=fa[x];rotate(x))
if(fa[faf])
rotate((gt(x)==gt(faf))?faf:x);
}
inline void sinsert(int i,int x)//在Splay里加点
{
int now=rt[i],faf=;
if (!rt[i])
{
rt[i]=++Size;
fa[Size]=son[Size][]=son[Size][]=;
size[Size]=cnt[Size]=; key[Size]=x;
return;
}
while("woxihuankeduoli")
{
if (x==key[now])
{
cnt[now]++;
pushup(faf);
Splay(now);
rt[i]=now;
return;
}
faf=now;
now=son[now][key[now]<x];
if(!now)
{
++Size;
fa[Size]=faf; son[Size][]=son[Size][]=;
size[Size]=cnt[Size]=; key[Size]=x;
son[faf][key[faf]<x]=Size;
pushup(faf);
Splay(Size);
rt[i]=Size;
return;
}
}
}
inline void sfind(int i,int x)
{
int now=rt[i];
while (){
if (key[now]==x)
{
Splay(now);
rt[i]=now;
return;
}
else if (key[now]>x) now=son[now][];
else if (key[now]<x) now=son[now][];
}
}
inline int spre(int i)
{
int now=son[rt[i]][];
while (son[now][]) now=son[now][];
return now;
}
inline int snext(int i)
{
int now=son[rt[i]][];
while (son[now][]) now=son[now][];
return now;
}
inline void sdel(int i)
{
int now=rt[i];
if (cnt[now]>)
{
cnt[now]--;
pushup(now);
return;
}
if (!son[now][]&&!son[now][])
{
sclear(rt[i]);
rt[i]=;
return;
}
if (!son[now][])
{
int prert=now;
rt[i]=son[prert][];
fa[rt[i]]=;
sclear(prert);
return;
}
if (!son[now][])
{
int prert=now;
rt[i]=son[prert][];
fa[rt[i]]=;
sclear(prert);
return;
}
int leftM=spre(i),prert=rt[i];
Splay(leftM); rt[i]=leftM;
son[rt[i]][]=son[prert][];
fa[son[prert][]]=rt[i];
sclear(prert);
pushup(rt[i]);
return;
}
inline int sfindrank(int i,int x)
{
int now=rt[i],ans=;
while ()
{
if (!now) return ans;
if (key[now]==x) return ((son[now][])?size[son[now][]]:)+ans;
else if (key[now]<x)
{
ans+=((son[now][])?size[son[now][]]:)+cnt[now];
now=son[now][];
}
else if (key[now]>x) now=son[now][];
}
}
inline int sfindpre(int i,int k)
{
int now=rt[i];
while (now)
{
if (key[now]<k)
{
if (ans<key[now])ans=key[now];
now=son[now][];
}
else now=son[now][];
}
return ans;
}
inline int sfindnext(int i,int k)
{
int now=rt[i];
while (now)
{
if (key[now]>k)
{
if (ans>key[now]) ans=key[now];
now=son[now][];
}
else now=son[now][];
}
return ans;
}
//以上为splay操作 下面是线段树操作
inline void insertSeg(int id,int l,int r,int x,int v)
{
int mid=(l+r)>>;
sinsert(id,v);
if (l==r) return;
if (x<=mid) insertSeg(id<<,l,mid,x,v);
else insertSeg(id<<|,mid+,r,x,v);
}
inline void askrankSeg(int id,int l,int r,int lrange,int rrange,int k)
{
int mid=(l+r)>>;
if (lrange<=l&&r<=rrange)
{
ans+=sfindrank(id,k);
return;
}
if (lrange<=mid) askrankSeg(id<<,l,mid,lrange,rrange,k);
if (mid+<=rrange) askrankSeg(id<<|,mid+,r,lrange,rrange,k);
}
inline void changeSeg(int id,int l,int r,int x,int k)
{
int mid=(l+r)>>;
sfind(id,a[x]); sdel(id); sinsert(id,k);
if (l==r) return;
if (x<=mid) changeSeg(id<<,l,mid,x,k);
else changeSeg(id<<|,mid+,r,x,k);
}
inline void askpreSeg(int id,int l,int r,int lrange,int rrange,int k)
{
int mid=(l+r)>>;
if (lrange<=l&&r<=rrange)
{
ans=max(ans,sfindpre(id,k));
return;
}
if (lrange<=mid) askpreSeg(id<<,l,mid,lrange,rrange,k);
if (mid+<=rrange) askpreSeg(id<<|,mid+,r,lrange,rrange,k);
}
inline void asknextSeg(int id,int l,int r,int lrange,int rrange,int k)
{
int mid=(l+r)>>;
if (lrange<=l&&r<=rrange)
{
ans=min(ans,sfindnext(id,k));
return;
}
if (lrange<=mid) asknextSeg(id<<,l,mid,lrange,rrange,k);
if (mid+<=rrange) asknextSeg(id<<|,mid+,r,lrange,rrange,k);
}
}Tree;
int _max=-;
int opt,l,r,k,le,ri,md;
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){scanf("%d",&a[i]);_max=max(_max,a[i]);Tree.insertSeg(,,n,i,a[i]);}
for(int i=;i<=m;i++)
{
scanf("%d",&opt);
if(opt==)
{
scanf("%d%d%d",&l,&r,&k);
ans=;
Tree.askrankSeg(,,n,l,r,k);
printf("%d\n",ans+);
}
if(opt==)
{
scanf("%d%d%d",&l,&r,&k);
le=,ri=_max+;
while(le!=ri)
{
md=(le+ri)>>;
ans=;
Tree.askrankSeg(,,n,l,r,md);
if(ans<k)le=md+;
else ri=md;
}
printf("%d\n",le-);
}
if(opt==)
{
scanf("%d%d",&l,&k);
Tree.changeSeg(,,n,l,k);
a[l]=k;
_max=max(_max,k);
}
if(opt==)
{
scanf("%d%d%d",&l,&r,&k);ans=-;
Tree.askpreSeg(,,n,l,r,k);
printf("%d\n",ans);
}
if(opt==)
{
scanf("%d%d%d",&l,&r,&k);ans=;
Tree.asknextSeg(,,n,l,r,k);
printf("%d\n",ans);
}
}
}
线段树套平衡树很简单(思想层面)但它是我们学习树套树的基础,建议仔细学习一个,然后做以下例题。
这个东西练熟了,我们就可以更好的了解其他形式的树套树,我们还可以更好的形成"树套树"的思想
这个思想有助于我们写出很多数据结构毒瘤题的正解/暴力
例题:
bzoj2141 排队 注:此题线段树套平衡树略难,可以参考网上的分块套树状数组做法
bzoj1901 ZOJ2112 Dynamic Rankings 注:此题也叫“可持久化主席树”所以绝对有树状数组套主席树的做法
bzoj2120 数颜色 注:正解带修改莫队
题解我会慢慢写,毕竟树套树写一个也不是那么简单啊QAQ
树套树Day1线段树套平衡树bzoj3196的更多相关文章
- luogu3380/bzoj3196 二逼平衡树 (树状数组套权值线段树)
带修改区间K大值 这题有很多做法,我的做法是树状数组套权值线段树,修改查询的时候都是按着树状数组的规则找出那log(n)个线段树根,然后一起往下做 时空都是$O(nlog^2n)$的(如果离散化了的话 ...
- 【bzoj3065】带插入区间K小值 替罪羊树套权值线段树
题目描述 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间k小值.他每次向它的随从伏特提出 ...
- BZOJ2141排队——树状数组套权值线段树(带修改的主席树)
题目描述 排排坐,吃果果,生果甜嗦嗦,大家笑呵呵.你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家 乐和和.红星幼儿园的小朋友们排起了长长地队伍,准备吃果果.不过因为小朋友们的身高有所区别 ...
- CF1093E Intersection of Permutations 树状数组套权值线段树
\(\color{#0066ff}{ 题目描述 }\) 给定整数 \(n\) 和两个 \(1,\dots,n\) 的排列 \(a,b\). \(m\) 个操作,操作有两种: \(1\ l_a\ r_a ...
- Dynamic Rankings(树状数组套权值线段树)
Dynamic Rankings(树状数组套权值线段树) 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[ ...
- [BZOJ 3295] [luogu 3157] [CQOI2011]动态逆序对(树状数组套权值线段树)
[BZOJ 3295] [luogu 3157] [CQOI2011] 动态逆序对 (树状数组套权值线段树) 题面 给出一个长度为n的排列,每次操作删除一个数,求每次操作前排列逆序对的个数 分析 每次 ...
- 【BZOJ3196】二逼平衡树(树状数组,线段树)
[BZOJ3196]二逼平衡树(树状数组,线段树) 题面 BZOJ题面 题解 如果不存在区间修改操作: 搞一个权值线段树 区间第K大--->直接在线段树上二分 某个数第几大--->查询一下 ...
- NOIp 数据结构专题总结 (2):分块、树状数组、线段树
系列索引: NOIp 数据结构专题总结 (1) NOIp 数据结构专题总结 (2) 分块 阅:<「分块」数列分块入门 1-9 by hzwer> 树状数组 Binary Indexed T ...
- 树(一)——线段树
问题 现在有1~30这30个数,数N被抽上的概率正比于1/sqrt(N+1),求满足这个概率分布的随机数发生器. 思路 第一,如何解决这个"概率正比"问题. 第二,如何产生满足条件 ...
随机推荐
- 25线程基础-CLR
由CLR via C#(第三版) ,摘抄记录... 1.线程是CPU的虚拟化,windows为每个进程提供专用线程(CPU)2.线程开销:内存和时间. 线程内核对象—OS为系统中创建的每个线程都分配并 ...
- C语言基础知识【作用域规则】
C 作用域规则1.任何一种编程中,作用域是程序中定义的变量所存在的区域,超过该区域变量就不能被访问.C 语言中有三个地方可以声明变量:在函数或块内部的局部变量在所有函数外部的全局变量在形式参数的函数参 ...
- 电路分析二-------基尔霍夫定律KCL和KVL
1.先了解几个名词 (1)支路----一个二端原件视为一条支路--图中6个二端原件所以有6条支路. (2)结点----两条或以上的支路连接的点. d,e可以看做一个结点. (3).回路----- (4 ...
- 9.接口BeanPostProcessor
package org.springframework.beans.factory.config; import org.springframework.beans.BeansException; p ...
- Hadoop初体验
1.首先准备环境 系统:Linux(centOS) jdk:1.7 这里jdk要安装配置完成,具体步骤参考:Linux环境下安装JDK 注意:本次没有配置免密登录,所以在启动和停止的时候回让你输入多次 ...
- centos7 使用postgres
1: http://www.cnblogs.com/think8848/p/5877076.html 2:http://blog.csdn.net/xuaa/article/details/52262 ...
- 拉普拉斯分布(Laplace distribution)
拉普拉斯分布的定义与基本性质 其分布函数为 分布函数图 其概率密度函数为 密度函数图 拉普拉斯分布与正太分布的比较 从图中可以直观的发现拉普拉斯分布跟正太分布很相似,但是拉普拉斯分布比正太分布有尖的峰 ...
- Android LockScreen (锁屏弹窗)
在要弹窗的Activity需要进行以下设置,才可以在锁屏状态下弹窗 @Override protected void onCreate(Bundle savedInstanceState) { fin ...
- ABAP 面向对象(Object Orientation) OO
[转自 http://blog.sina.com.cn/s/blog_7c7b16000101bhof.html]在程序中, 对象的识别和寻址是通过对象引用来实现的, 对象引用变量可以访问对象的属性和 ...
- 【HTTP】HTPP学习笔记
1.了解web及网络基础 HTTP的诞生 TCP/IP协议族 应用层 FTP文件传输协议 HTTP超文本传输协议 DNS域名系统:IP地址<--->域名 传输层 TCP传输控制协议 三次握 ...