题目链接

  幸甚至哉,歌以咏志。

  拿下了曾经是那么遥不可及的线段树,学会了曾经高不可攀的平衡树,弄懂了装B的时候才挂在嘴边的树套树。

  每道模板都是链上的一颗珠子。把它们挨个串起来,就成为我成长的历程。

  抒情结束开始讲题

  这道题我们用线段树存平衡树的根节点。比如我们有一棵线段树

  

  这样子。线段树的一个节点   存   它表示的那个区间   所对应的  平衡树   的根节点编号。这样每个节点都拥有一棵平衡树。是不是很炫呢?

  对于操作1我们就可以把所有零散的区间里比它小的数的个数都找出来,+1就是答案啦。

  对于操作2我们可以二分数,然后不断地进行操作1.

  对于操作3我们用logn的时间把所有包含这个点的区间都修改一遍。

  对于操作4和操作5,不多讲了。

  很炫吧

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#define mid ((l+r)>>1)
#define left (rt<<1)
#define right (rt<<1|1)
#define lson l,mid,left
#define rson mid+1,r,right using std::max;
using std::min; inline int read(){
int num=,f=;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-;
ch=getchar();
}
while(isdigit(ch)){
num=num*+ch-'';
ch=getchar();
}
return num*f;
} int s[];
int q[]; struct Node{
int e[],fa,val,size,sum;
}tree[];
int tot,point;
inline void update(int x){
tree[x].size=tree[x].sum;
if(tree[x].e[]) tree[x].size+=tree[tree[x].e[]].size;
if(tree[x].e[]) tree[x].size+=tree[tree[x].e[]].size;
}
inline void connect(int x,int fa,int how){ tree[x].fa=fa; tree[fa].e[how]=x; }
inline int iden(int x){ return x==tree[tree[x].fa].e[]; }
void rotate(int x,int rt){
int y=tree[x].fa; int r=tree[y].fa;
if(y==s[rt]) s[rt]=x;
int sony=iden(x); int sonr=iden(y);
int b=tree[x].e[sony^];
connect(b,y,sony);
connect(y,x,sony^);
connect(x,r,sonr);
update(y); update(x);
} void splay(int pos,int to,int rt){
to=tree[to].fa;
while(tree[pos].fa!=to){
if(tree[tree[pos].fa].fa==to) rotate(pos,rt);
else
if(iden(pos)==iden(tree[pos].fa)){ rotate(tree[pos].fa,rt); rotate(pos,rt); }
else { rotate(pos,rt); rotate(pos,rt); }
}
} inline int create(int val,int fa){
tree[++tot].val=val;
tree[tot].fa=fa;
tree[tot].sum=tree[tot].size=;
return tot;
} inline void Delete(int x){
tree[x].e[]=tree[x].e[]=;
if(x==tot) tot--;
} int build(int val,int rt){
point++;
if(!s[rt]){ s[rt]=create(val,); return s[rt];}
else{
int now=s[rt];
while(){
tree[now].size++;
if(val==tree[now].val){ tree[now].sum++; return now; }
int nxt=val<tree[now].val?:;
if(!tree[now].e[nxt]){
create(val,now);
tree[now].e[nxt]=tot;
return tot;
}
now=tree[now].e[nxt];
}
}
return ;
} inline void insert(int val,int rt){
int p=build(val,rt);
splay(p,s[rt],rt);
} int find(int val,int rt){
int now=s[rt];
while(now){
if(tree[now].val==val){ splay(now,s[rt],rt); return now; }
int nxt=val>tree[now].val;
if(!tree[now].e[nxt]) return ;
now=tree[now].e[nxt];
}
} void pop(int val,int rt){
int deal=find(val,rt);
if(!deal) return;
point--;
if(tree[deal].sum>){ tree[deal].sum--; tree[deal].size--; return; }
if(!tree[deal].e[]){ s[rt]=tree[deal].e[]; tree[s[rt]].fa=; }
else{
int le=tree[deal].e[];
while(tree[le].e[]) le=tree[le].e[];
splay(le,tree[deal].e[],rt);
int ri=tree[deal].e[];
connect(ri,le,); s[rt]=le;
update(le);
}
Delete(deal);
} int rank(int val,int rt){
int ans=,now=s[rt];
while(){
//printf("%d %d\n",now,tree[now].sum);
if(val<tree[now].val){
now=tree[now].e[];
if(!now) return ans;
}
else{
if(tree[now].e[]) ans+=tree[tree[now].e[]].size;
if(val==tree[now].val||!tree[now].e[]){
if(val>tree[now].val) ans+=tree[now].sum;
splay(now,s[rt],rt);
return ans;
}
ans+=tree[now].sum; now=tree[now].e[];
}
}
} inline int lower(int val,int rt){
int ans=-,now=s[rt];
while(){
if(!now) return ans;
if(tree[now].val<val&&tree[now].val>ans) ans=tree[now].val;
int nxt=val>tree[now].val?:;
now=tree[now].e[nxt];
}
} inline int upper(int val,int rt){
int ans=,now=s[rt];
while(){
if(!now) return ans;
if(tree[now].val>val&&tree[now].val<ans) ans=tree[now].val;
int nxt=val>tree[now].val?:;
now=tree[now].e[nxt];
}
} int lows(int val,int rt){
int ans=-,now=s[rt];
while(){
if(!now) return ans;
if(tree[now].val<=val&&tree[now].val>ans) ans=tree[now].val;
if(tree[now].val==val) return ans;
int nxt=val>tree[now].val?:;
now=tree[now].e[nxt];
}
} void Build(int l,int r,int rt){
if(l>r) return;
if(l==r){
insert(q[l],rt);
return;
}
Build(lson);
Build(rson);
for(int i=l;i<=r;++i) insert(q[i],rt);
return;
} int findrank(int from,int to,int num,int l,int r,int rt){
if(from<=l&&to>=r) return rank(num,rt);
int ans=;
if(from<=mid) ans+=findrank(from,to,num,lson);
if(to>mid) ans+=findrank(from,to,num,rson);
return ans;
} void Update(int o,int num,int l,int r,int rt){
pop(q[o],rt);
insert(num,rt);
if(l==r) return;
if(o<=mid) Update(o,num,lson);
else Update(o,num,rson);
} int findlower(int from,int to,int num,int l,int r,int rt){
if(from<=l&&to>=r) return lower(num,rt);
int ans=-;
if(from<=mid) ans=max(ans,findlower(from,to,num,lson));
if(to>mid) ans=max(ans,findlower(from,to,num,rson));
return ans;
} int findupper(int from,int to,int num,int l,int r,int rt){
if(from<=l&&to>=r) return upper(num,rt);
int ans=;
if(from<=mid) ans=min(ans,findupper(from,to,num,lson));
if(to>mid) ans=min(ans,findupper(from,to,num,rson));
return ans;
} int main(){
int n=read(),m=read();
for(int i=;i<=n;++i) q[i]=read();
Build(,n,);
for(register int i=;i<=m;++i){
int opt=read();
if(opt==){
int l=read(),r=read(),q=read();
printf("%d\n",findrank(l,r,q,,n,)+);
}
else if(opt==){
int l=read(),r=read(),q=read();
int a=,b=1e8,Ans=;
while(a<=b){
int m=(a+b)>>;
int x=lows(m,);
if(findrank(l,r,x,,n,)+>q) b=m-;
else{
a=m+;
Ans=x;
}
}
printf("%d\n",Ans);
}
else if(opt==){
int l=read(),r=read();
Update(l,r,,n,);
q[l]=r;
}
else if(opt==){
int l=read(),r=read(),q=read();
printf("%d\n",findlower(l,r,q,,n,));
}
else if(opt==){
int l=read(),r=read(),q=read();
printf("%d\n",findupper(l,r,q,,n,));
}
}
return ;
}

【Luogu】P3380树套树模板(线段树套Splay)的更多相关文章

  1. [BZOJ 3295] [luogu 3157] [CQOI2011]动态逆序对(树状数组套权值线段树)

    [BZOJ 3295] [luogu 3157] [CQOI2011] 动态逆序对 (树状数组套权值线段树) 题面 给出一个长度为n的排列,每次操作删除一个数,求每次操作前排列逆序对的个数 分析 每次 ...

  2. 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 ...

  3. BZOJ2141排队——树状数组套权值线段树(带修改的主席树)

    题目描述 排排坐,吃果果,生果甜嗦嗦,大家笑呵呵.你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家 乐和和.红星幼儿园的小朋友们排起了长长地队伍,准备吃果果.不过因为小朋友们的身高有所区别 ...

  4. luogu3380/bzoj3196 二逼平衡树 (树状数组套权值线段树)

    带修改区间K大值 这题有很多做法,我的做法是树状数组套权值线段树,修改查询的时候都是按着树状数组的规则找出那log(n)个线段树根,然后一起往下做 时空都是$O(nlog^2n)$的(如果离散化了的话 ...

  5. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  6. CF1093E Intersection of Permutations 树状数组套权值线段树

    \(\color{#0066ff}{ 题目描述 }\) 给定整数 \(n\) 和两个 \(1,\dots,n\) 的排列 \(a,b\). \(m\) 个操作,操作有两种: \(1\ l_a\ r_a ...

  7. Dynamic Rankings(树状数组套权值线段树)

    Dynamic Rankings(树状数组套权值线段树) 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[ ...

  8. 【bzoj3065】带插入区间K小值 替罪羊树套权值线段树

    题目描述 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间k小值.他每次向它的随从伏特提出 ...

  9. Luogu P4246 [SHOI2008]堵塞的交通(线段树+模拟)

    P4246 [SHOI2008]堵塞的交通 题意 题目描述 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可以被看成是一个\(2\)行\(C\)列的矩形 ...

  10. luogu P6088 [JSOI2015]字符串树 可持久化trie 线段树合并 树链剖分 trie树

    LINK:字符串树 先说比较简单的正解.由于我没有从最简单的考虑答案的角度思考 所以... 下次还需要把所有角度都考察到. 求x~y的答案 考虑 求x~根+y~根-2*lca~根的答案. 那么问题变成 ...

随机推荐

  1. java25个Java机器学习工具&库

    本列表总结了25个Java机器学习工具&库: 1. Weka集成了数据挖掘工作的机器学习算法.这些算法可以直接应用于一个数据集上或者你可以自己编写代码来调用.Weka包括一系列的工具,如数据预 ...

  2. 洛谷 P2347 砝码称重 != codevs 2144

    题目描述 设有1g.2g.3g.5g.10g.20g的砝码各若干枚(其总重<=1000), 输入输出格式 输入格式: 输入方式:a1 a2 a3 a4 a5 a6 (表示1g砝码有a1个,2g砝 ...

  3. 用函数创建对象、类创建对象,以及使用prototype的好处

    用函数创建对象 var CheckObject = function(){}; CheckObject.checkName = function(){ // 检验姓名 }; CheckObject.c ...

  4. 79 最长公共子串 (lintcode)

    f[i][j]表示的是以第i个结尾和第j个结尾 class Solution { public: /* * @param A: A string * @param B: A string * @ret ...

  5. python3中bytes、hex和字符串相互转换

    1.字符串转bytes a = 'abcd' a1 = bytes(a,encoding('utf-8')) 2.bytes转字符串 a = b'abcd' a1 = bytes.decode(a , ...

  6. 来自-小坦克:Fiddler教程

    Fiddler 教程 阅读目录 Fiddler的基本介绍 Fiddler的工作原理 同类的其它工具 Fiddler如何捕获Firefox的会话 Fiddler如何捕获HTTPS会话 Fiddler的基 ...

  7. [BZOJ3307]:雨天的尾巴(LCA+树上差分+权值线段树)

    题目传送门 题目描述: N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入格式: 第一 ...

  8. 项目中多条数据保存的json实例

    //js代码function checkCode(num){ var typeid = $("#typeid").val(); if(typeid == "") ...

  9. 总结 Swift 中随机数的使用

    在我们开发的过程中,时不时地需要产生一些随机数.这里我们总结一下Swift中常用的一些随机数生成函数.这里我们将在Playground中来做些示例演示. 整型随机数 如果我们想要一个整型的随机数,则可 ...

  10. 【Java_多线程并发编程】JUC原子类——4种原子类

    根据修改的数据类型,可以将JUC包中的原子操作类可以分为4种,分别是: 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ;2. 数组类型: Atom ...