bzoj 3196 Tyvj 1730 二逼平衡树(线段树套名次树)

时间:2023-03-08 18:01:28
bzoj 3196 Tyvj 1730 二逼平衡树(线段树套名次树)

3196: Tyvj 1730 二逼平衡树

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1807  Solved: 772
[Submit][Status][Discuss]

Description

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)

Input

第一行两个数 n,m 表示长度为n的有序序列和m个操作
第二行有n个数,表示有序序列
下面有m行,opt表示操作标号
若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名
若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数
若opt=3 则为操作3,之后有两个数pos,k 表示将pos位置的数修改为k
若opt=4 则为操作4,之后有三个数l,r,k 表示查询区间[l,r]内k的前驱
若opt=5 则为操作5,之后有三个数l,r,k 表示查询区间[l,r]内k的后继

Output

对于操作1,2,4,5各输出一行,表示查询结果

Sample Input

9 6
4 2 2 1 9 4 0 1 1
2 1 4 3
3 4 10
2 1 4 3
1 2 5 9
4 3 9 5
5 2 8 5

Sample Output

2
4
3
4
9

HINT

1.n和m的数据范围:n,m<=50000

2.序列中每个数的数据范围:[0,1e8]

3.虽然原题没有,但事实上5操作的k可能为负数

【思路】

线段树套名次树

名次树上的各个操作和普通平衡树大致一样。

一棵线段树,每一个节点为一棵Treap,各操作如下:

操作1 -- 找出[l,r]内k的rank,得到比k小的数量s,则答案为s+1,在区间内统计比k小的数量即可。

操作2 – 找出[l,r]内rank为k的数,二分查找该数M,通过操作1可以得到数M的rank,根据k与rank调整区间。

操作3 – 修改pos上的数为k,在线段树上递归到位置pos,修改路径上的每一棵Treap,对应一次remove与一次insert。

操作4 – 找出[l,r]内k的前驱,在区间每一棵包含区间的Treap上进行Treap中查找前驱的操作,更新ans。

操作5 – 同理4

【代码】

 #include<cstdio>
#include<ctime>
#include<cstring>
#include<cstdlib>
#include<iostream>
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
using namespace std; const int N = *1e5+;
const int INF = 1e8; int n,m,val[N],tmp;
//RANK TREE
struct Node {
Node* ch[];
int v,r,s,w;
Node(int x):v(x) {ch[]=ch[]=NULL; s=w=; r=rand();}
void maintain() {
s=w;
if(ch[]!=NULL) s+=ch[]->s;
if(ch[]!=NULL) s+=ch[]->s;
}
int cmp(int x) {
if(x==v) return -; else return x<v? :;
}
};
void rotate(Node* &o,int d) {
Node* k=o->ch[d^]; o->ch[d^]=k->ch[d],k->ch[d]=o;
o->maintain(),k->maintain(); o=k;
}
void insert(Node* &o,int x) {
if(o==NULL) o=new Node(x);
else {
int d=o->cmp(x);
if(d==-) { o->w++; o->maintain(); return; }
insert(o->ch[d],x);
if(o->ch[d]->r > o->r) rotate(o,d^);
}
o->maintain();
}
void remove(Node* &o,int x) {
if(o==NULL) return;
int d=o->cmp(x);
if(d==-) {
Node* u=o;
if(o->w>) { o->w--;o->maintain();return; }
if(o->ch[]!=NULL&&o->ch[]!=NULL) {
int d2=o->ch[]->r>o->ch[]->r? :;
rotate(o,d2),remove(o->ch[d2],x);
}
else {
if(o->ch[]!=NULL) o=o->ch[]; else o=o->ch[];
delete u;
}
} else remove(o->ch[d],x);
if(o!=NULL) o->maintain();
}
void rank(Node* o,int x) {
if(o==NULL) return ;
int d=o->cmp(x),s=o->ch[]==NULL? :o->ch[]->s;
if(d==-) { tmp+=s; return ; }
else if(d==) rank(o->ch[],x);
else { tmp+=s+o->w; rank(o->ch[],x); }
}
void before(Node* o,int x) {
if(o==NULL) return ;
if(o->v<x) { tmp=max(tmp,o->v); before(o->ch[],x); }
else before(o->ch[],x);
}
void after(Node* o,int x) {
if(o==NULL) return ;
if(o->v>x) { tmp=min(tmp,o->v); after(o->ch[],x); }
else after(o->ch[],x);
}
//SEGMENT TREE 2D
Node* root[N>>];
void build(int u,int L,int R,int r,int x) {
insert(root[u],x);
if(L==R) return ;
int M=(L+R)>>;
if(r<=M) build(u<<,L,M,r,x);
else build(u<<|,M+,R,r,x);
}
void getrank(int u,int L,int R,int l,int r,int x) {
if(l==L && r==R) { rank(root[u],x); return ; }
int M=(L+R)>>;
if(r<=M) getrank(u<<,L,M,l,r,x);
else if(M<l) getrank(u<<|,M+,R,l,r,x);
else {
getrank(u<<,L,M,l,M,x);
getrank(u<<|,M+,R,M+,r,x);
}
}
void getindex(int x,int y,int k) {
int num,L=,R=INF,M;
while(L<=R) {
int M=(L+R)>>;
tmp=;
getrank(,,n,x,y,M);
if(tmp<=k) L=M+,num=M;
else R=M-;
}
printf("%d\n",num);
}
void change(int u,int L,int R,int r,int x,int y) {
remove(root[u],x),insert(root[u],y);
if(L==R) return ;
int M=(L+R)>>;
if(r<=M) change(u<<,L,M,r,x,y);
else change(u<<|,M+,R,r,x,y);
}
void getbefore(int u,int L,int R,int l,int r,int x) {
if(l==L && R==r) { before(root[u],x);return ;}
int M=(L+R)>>;
if(r<=M) getbefore(u<<,L,M,l,r,x);
else if(l>M) getbefore(u<<|,M+,R,l,r,x);
else {
getbefore(u<<,L,M,l,M,x);
getbefore(u<<|,M+,R,M+,r,x);
}
}
void getafter(int u,int L,int R,int l,int r,int x) {
if(l==L && R==r) { after(root[u],x); return ;}
int M=(L+R)>>;
if(r<=M) getafter(u<<,L,M,l,r,x);
else if(l>M) getafter(u<<|,M+,R,l,r,x);
else {
getafter(u<<,L,M,l,M,x);
getafter(u<<|,M+,R,M+,r,x);
}
}
void read(int& x) {
char c=getchar(); int f=; x=;
while(!isdigit(c)) {if(c=='-')f=-;c=getchar();}
while(isdigit(c)) x=x*+c-'',c=getchar();
x*=f;
}
void trav(Node* o) {
if(o==NULL) return ;
FOR(i,,o->w) cout<<o->v<<" ";
trav(o->ch[]),trav(o->ch[]);
}
int main() {
//freopen("in.in","r",stdin);
//freopen("out.out","w",stdout);
read(n),read(m);
FOR(i,,n) read(val[i]),build(,,n,i,val[i]);
int op,a,b,c;
while(m--) {
read(op),read(a),read(b);
switch(op) {
case :
read(c); tmp=; getrank(,,n,a,b,c);
printf("%d\n",tmp);
break;
case :
read(c); getindex(a,b,c);
break;
case :
change(,,n,a,val[a],b); val[a]=b;
break;
case :
read(c); tmp=; getbefore(,,n,a,b,c);
printf("%d\n",tmp);
break;
case :
read(c); tmp=INF; getafter(,,n,a,b,c);
printf("%d\n",tmp);
break;
}
}
return ;
}