看的是洛谷日报上的教程 结果因为求第k大时那个判断就出了错导致时间超限 惨烈
然后还要注意开始要插入一个极大值和极小值
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /* 2 id:gww 3 language:C-- 4 5 */ 6 #include<bits/stdc++.h> 7 using namespace std; 8 const int N=200005; 9 int ch[N][2],val[N],cnt[N],par[N],size[N]; 10 int root,ncnt=0; 11 //0 1代表 x的左 右儿子 val[x] x存储的值 12 //cnt[x] x存储的重复权值的个数 par[x]代表 x的父节点 13 //size[x]代表 x子树下的储存的权值数(包括重复权值) 14 inline int rd() 15 { 16 int x=0,w=0;char ch=0; 17 while(!isdigit(ch)) w|=ch=='-',ch=getchar(); 18 while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); 19 return w?-x:x; 20 } 21 22 void pushup(int x) {size[x]=size[ch[x][0]]+size[ch[x][1]]+cnt[x];}//更新size数组 23 bool chk(int x) {return ch[par[x]][1]==x;}//判断x位于它父亲的方向 24 25 void rotate(int x)//旋转 将被指定节点向上移动一级 26 { 27 int y=par[x],z=par[y],k=chk(x),w=ch[x][k^1]; 28 ch[y][k]=w;par[w]=y; 29 //父节点连向需旋转的该子节点的方向的边连向该子节点位于其父节点方向的反方向的节点 30 ch[z][chk(y)]=x;par[x]=z; 31 //爷爷节点连向父节点的边连向需旋转的该节点 32 ch[x][k^1]=y;par[y]=x; 33 //需旋转的该节点连向该子节点位于其父节点方向的反方向的子节点的边连向其父节点 34 pushup(y);pushup(x); 35 } 36 37 void splay(int x,int goal=0)//伸展 38 { 39 while(par[x]!=goal) 40 { 41 int y=par[x],z=par[y]; 42 if(z!=goal) 43 { 44 if(chk(x)==chk(y)) rotate(y);//三点一线 转父亲 45 else rotate(x); 46 } 47 rotate(x); 48 } 49 if(!goal) root=x; 50 } 51 52 void find(int x)//将最大的小于等于x的数所在的节点splay到根 53 { 54 // if(!root) return; 55 int cur=root; 56 while(val[cur]!=x&&ch[cur][x>val[cur]]) {cur=ch[cur][x>val[cur]];} 57 splay(cur); 58 } 59 60 void insert(int x)//插入 61 { 62 int cur=root,p=0;//从根节点开始 63 while(val[cur]!=x&&cur) {p=cur;cur=ch[cur][x>val[cur]];} //不存在 新建节点并与父节点连边 64 if(cur) cnt[cur]++;//节点存在则直接自增cnt的值 65 else//新建节点时可能会拉出一条链, 66 { 67 cur=++ncnt; 68 if(p) ch[p][x>val[p]]=cur; 69 par[cur]=p;val[cur]=x; 70 ch[cur][0]=ch[cur][1]=0; 71 size[cur]=cnt[cur]=1; 72 } 73 splay(cur);//所以新建节点后需要将该节点splay到根节点 74 } 75 76 int kth(int k) 77 { 78 int cur=root; 79 while(1) 80 { 81 if (ch[cur][0] && k <= size[ch[cur][0]]) {cur = ch[cur][0];} 82 else if (k > size[ch[cur][0]] + cnt[cur]) 83 { 84 k-=size[ch[cur][0]]+cnt[cur]; 85 cur=ch[cur][1]; 86 } 87 else {return cur;} 88 } 89 } 90 91 int pre(int x)//前驱 小于这个值并且最接近这个值的元素值 92 { 93 find(x);//先初始化一个最值 94 if(val[root]<x) return root; 95 int cur=ch[root][0];//向下走 96 while(ch[cur][1]) cur=ch[cur][1]; 97 return cur; 98 } 99 int succ(int x)//后继 大于这个值并且最接近这个值的元素值 100 { 101 find(x);//先初始化一个最值 102 if(val[root]>x) return root; 103 int cur=ch[root][1];//向下走 104 while(ch[cur][0]) cur=ch[cur][0]; 105 return cur; 106 } 107 108 void remove(int x) 109 {//把前驱splay到根,后继splay到前驱的右儿子,那么后继的左儿子就是要删除的点 110 int last=pre(x),nxt=succ(x); 111 splay(last);splay(nxt,last); 112 int del=ch[nxt][0]; 113 if(cnt[del]>1) {cnt[del]--;splay(del);} 114 else {ch[nxt][0]=0;} 115 } 116 117 int getrank(int x) 118 { 119 find(x); 120 return size[ch[root][0]]; 121 } 122 123 int main() 124 { 125 int t=rd(); 126 insert(1e9); 127 insert(-1e9); 128 while(t--) 129 { 130 int opt=rd(),x=rd(); 131 if(opt==1) insert(x); 132 if(opt==2) remove(x); 133 if(opt==3) printf("%d\n",getrank(x)); 134 if(opt==4) printf("%d\n",val[kth(x+1)]); 135 if(opt==5) printf("%d\n",val[pre(x)]); 136 if(opt==6) printf("%d\n",val[succ(x)]); 137 } 138 return 0; 139 }