【BZOJ3196】Tyvj 1730 二逼平衡树
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
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
4
3
4
9
HINT
1.n和m的数据范围:n,m<=50000
2.序列中每个数的数据范围:[0,1e8]
3.虽然原题没有,但事实上5操作的k可能为负数
题解
树套树,调了好久,至今都不是很清楚为什么删除节点的时候不能直接减一而必须先....说不清楚了,在代码里体现吧
1 #include<cstdio> 2 #include<cstdlib> 3 #include<iostream> 4 using namespace std; 5 const int inf=100000000,N=3000001,M=200001; 6 int num[N],rnd[N],size[N],ls[N],rs[N],root[M],w[N],a[M]; 7 int sz,n,m,opt,l,r; 8 void updata(int k){size[k]=size[ls[k]]+size[rs[k]]+w[k];} 9 void rturn(int &k){int t=ls[k];ls[k]=rs[t];rs[t]=k;size[t]=size[k];updata(k);k=t;} 10 void lturn(int &k){int t=rs[k];rs[k]=ls[t];ls[t]=k;size[t]=size[k];updata(k);k=t;} 11 void insert(int &k,int x){ 12 if (!k){ 13 k=++sz;num[k]=x;rnd[k]=rand();size[k]=w[k]=1;return; 14 } 15 size[k]++; 16 if (x==num[k]) {w[k]++;return;} 17 if (x<num[k]) {insert(ls[k],x);if (rnd[ls[k]]<rnd[k]) rturn(k);} 18 if (x>num[k]) {insert(rs[k],x);if (rnd[rs[k]]<rnd[k]) lturn(k);} 19 } 20 21 void del(int &k,int x){ 22 if (!k) return; 23 if (num[k]==x){ 24 if (w[k]>1){w[k]--;size[k]--;return;}//就是这里,不知道为什么不是在前面直接size[k]-- 25 if(ls[k]*rs[k]==0){k=ls[k]+rs[k]; return;} 26 if (rnd[ls[k]]<rnd[rs[k]])rturn(k),del(k,x); 27 else lturn(k),del(k,x); 28 } 29 else if (num[k]>x) del(ls[k],x),size[k]--;else del(rs[k],x),size[k]--; 30 } 31 32 void change(int pos,int x){ 33 int k=1,l=1,r=n,mid=(l+r)>>1; 34 while (l!=r){ 35 mid=(l+r)>>1; 36 del(root[k],a[pos]); 37 insert(root[k],x); 38 if (mid>=pos)k=k<<1,r=mid; 39 else k=k<<1|1,l=mid+1; 40 } 41 mid=(l+r)>>1; 42 del(root[k],a[pos]); 43 insert(root[k],x); 44 } 45 46 int solve_rank(int k,int x){ 47 if (!k) return 0; 48 int l=ls[k],r=rs[k]; 49 if (num[k]==x) return size[l]; 50 else if (num[k]>x) return solve_rank(l,x); 51 else return solve_rank(r,x)+size[l]+w[k]; 52 } 53 54 int get_rank(int k,int l,int r,int L,int R,int x){ 55 if (l==L&&r==R) return (solve_rank(root[k],x)); 56 int mid=(l+r)>>1; 57 if (R<=mid) return get_rank(k<<1,l,mid,L,R,x); 58 else if (L>mid) return get_rank(k<<1|1,mid+1,r,L,R,x); 59 else return get_rank(k<<1,l,mid,L,mid,x)+get_rank(k<<1|1,mid+1,r,mid+1,R,x); 60 } 61 62 void build(int x,int y){ 63 int l=1,r=n,k=1,mid=(l+r)>>1; 64 while(l!=r){ 65 insert(root[k],y); 66 if (mid>=x) r=mid,mid=(l+r)>>1,k=k<<1; 67 else l=mid+1,mid=(l+r)>>1,k=k<<1|1; 68 } 69 insert(root[k],y); 70 } 71 72 int get_num(int x,int y,int z){ 73 int l=1,r=inf,ans; 74 while (l<=r){ 75 int mid=(l+r)>>1; 76 int tmp=get_rank(1,1,n,x,y,mid); 77 if (tmp<=z) ans=mid,l=mid+1;//因为有重复的数字,tmp偏小 78 else r=mid-1; 79 } 80 return ans; 81 } 82 83 int solve_pre(int k,int x){ 84 int l=ls[k],r=rs[k]; 85 if (!k) return 0; 86 if (num[k]<x) return max(num[k],solve_pre(r,x));//这里不要顺手打上= 87 else return solve_pre(l,x); 88 } 89 90 int get_pre(int k,int l,int r,int L,int R,int x){ 91 if (l==L&&r==R) return (solve_pre(root[k],x)); 92 int mid=(l+r)>>1; 93 if (R<=mid) return get_pre(k<<1,l,mid,L,R,x); 94 else if (L>mid) return get_pre(k<<1|1,mid+1,r,L,R,x); 95 else return max(get_pre(k<<1,l,mid,L,mid,x),get_pre(k<<1|1,mid+1,r,mid+1,R,x)); 96 } 97 98 int solve_after(int k,int x){ 99 int l=ls[k],r=rs[k]; 100 if (!k) return inf; 101 if (num[k]>x) return min(num[k],solve_after(l,x));//这里不要顺手打上=*2 102 else return solve_after(r,x); 103 } 104 105 int get_after(int k,int l,int r,int L,int R,int x){ 106 if (l==L&&r==R) return (solve_after(root[k],x)); 107 int mid=(l+r)>>1; 108 if (R<=mid) return get_after(k<<1,l,mid,L,R,x); 109 else if (L>mid) return get_after(k<<1|1,mid+1,r,L,R,x); 110 else return min(get_after(k<<1|1,mid+1,r,mid+1,R,x),get_after(k<<1,l,mid,L,mid,x)); 111 } 112 113 int main(){ 114 freopen("sj.txt","r",stdin); 115 freopen("me.txt","w",stdout); 116 int k; 117 scanf("%d%d",&n,&m); 118 for (int i=1;i<=n;i++){scanf("%d",&a[i]);build(i,a[i]);} 119 for (int i=1;i<=m;i++){ 120 scanf("%d%d%d",&opt,&l,&r); 121 if (opt!=3)scanf("%d",&k); 122 switch(opt){ 123 case 1:printf("%d\n",get_rank(1,1,n,l,r,k)+1);break; 124 case 2:printf("%d\n",get_num(l,r,k-1));break; 125 case 3:change(l,r);a[l]=r;break; 126 case 4:printf("%d\n",get_pre(1,1,n,l,r,k));break; 127 case 5:printf("%d\n",get_after(1,1,n,l,r,k));break; 128 } 129 } 130 }