题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
-
插入x数
-
删除x数(若有多个相同的数,因只删除一个)
-
查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
-
查询排名为x的数
-
求x的前驱(前驱定义为小于x,且最大的数)
- 求x的后继(后继定义为大于x,且最小的数)
输入输出格式
输入格式:第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号( 1≤opt≤6 1 \leq opt \leq 6 1≤opt≤6 )
输出格式:对于操作3,4,5,6每行输出一个数,表示对应答案
输入输出样例
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
106465
84185
492737
说明
时空限制:1000ms,128M
1.n的数据范围: n≤100000
2.每个数的数据范围: [−107,107]
来源:Tyvj1728 原名:普通平衡树
在此鸣谢
Solution:
本题需要的6种操作的splay实现原理见此
splay代码:
1 #include<cstdio> 2 #define N 100010 3 using namespace std; 4 5 int fa[N],ch[N][2],siz[N],cnt[N],date[N],root,nn,n,tot; 6 7 int son(int x) { 8 return x==ch[fa[x]][1]; 9 } 10 11 void pushup(int rt) { 12 int l=ch[rt][0],r=ch[rt][1]; 13 siz[rt]=siz[l]+siz[r]+cnt[rt]; 14 } 15 16 void rotate(int x) { 17 int y=fa[x],z=fa[y],b=son(x),c=son(y),a=ch[x][!b]; 18 if(z)ch[z][c]=x;else root=x;fa[x]=z; 19 if(a)fa[a]=y;ch[y][b]=a; 20 ch[x][!b]=y;fa[y]=x;/// 21 pushup(y);pushup(x); 22 } 23 24 void splay(int x,int i) { 25 while(fa[x]!=i) { 26 int y=fa[x],z=fa[y]; 27 if(z==i) { 28 rotate(x); 29 } else { 30 if(son(x)==son(y)) { 31 rotate(y);rotate(x); 32 } else { 33 rotate(x);rotate(x); 34 } 35 } 36 } 37 } 38 39 void ins(int &rt,int x) { 40 if(rt==0) { 41 rt=++nn; 42 date[nn]=x; 43 siz[nn]=cnt[nn]=1; 44 return; 45 } 46 if(x==date[rt]) { 47 cnt[rt]++;siz[rt]++; 48 return; 49 } 50 if(x<date[rt]) { 51 ins(ch[rt][0],x); 52 fa[ch[rt][0]]=rt; 53 pushup(rt); 54 } else { 55 ins(ch[rt][1],x); 56 fa[ch[rt][1]]=rt; 57 pushup(rt); 58 } 59 } 60 61 int getpre(int rt,int x) { 62 int p=rt,ans; 63 while(p) { 64 if(x<=date[p]) { 65 p=ch[p][0];/// 66 } else { 67 ans=p; 68 p=ch[p][1]; 69 } 70 } 71 return ans; 72 } 73 74 int getsuc(int rt,int x) { 75 int p=rt,ans; 76 while(p) { 77 if(x>=date[p]) { 78 p=ch[p][1]; 79 } else { 80 ans=p; 81 p=ch[p][0]; 82 } 83 } 84 return ans; 85 } 86 87 int getmn(int rt) { 88 int p=rt,ans=-1; 89 while(p) { 90 ans=p; 91 p=ch[p][0]; 92 } 93 return ans; 94 } 95 96 void del(int rt,int x) { 97 if(date[rt]==x) { 98 if(cnt[rt]>1) { 99 cnt[rt]--;siz[rt]--; 100 } else { 101 splay(rt,0); 102 int p=getmn(ch[rt][1]); 103 if(p!=-1) { 104 splay(p,rt); 105 root=p;fa[p]=0; 106 ch[p][0]=ch[rt][0];fa[ch[rt][0]]=p; 107 pushup(p); 108 } else { 109 root=ch[rt][0];fa[ch[rt][0]]=0; 110 } 111 } 112 return; 113 } 114 if(x<date[rt]) { 115 del(ch[rt][0],x); 116 pushup(rt);/// 117 } else { 118 del(ch[rt][1],x); 119 pushup(rt); 120 } 121 } 122 123 int getk(int rt,int k) { 124 if(date[rt]==k) { 125 splay(rt,0); 126 if(ch[rt][0]==0) { 127 return 1; 128 } else { 129 return siz[ch[rt][0]]+1; 130 } 131 } 132 if(k<date[rt]) return getk(ch[rt][0],k); 133 else return getk(ch[rt][1],k); 134 } 135 136 int getkth(int rt,int k) { 137 int l=ch[rt][0]; 138 if(siz[l]+1<=k&&k<=siz[l]+cnt[rt]) return date[rt]; 139 if(k<siz[l]+1) return getkth(ch[rt][0],k); 140 if(siz[l]+cnt[rt]<k) return getkth(ch[rt][1],k-(siz[l]+cnt[rt])); 141 } 142 143 int main() { 144 scanf("%d",&n); 145 while(n--) { 146 int opt,x; 147 scanf("%d%d",&opt,&x); 148 if(opt==1) { 149 tot++; 150 ins(root,x); 151 } 152 if(opt==2) { 153 tot--; 154 del(root,x); 155 } 156 if(opt==3) { 157 printf("%d\n",getk(root,x)); 158 } 159 if(opt==4) { 160 printf("%d\n",getkth(root,x)); 161 } 162 if(opt==5) { 163 printf("%d\n",date[getpre(root,x)]); 164 } 165 if(opt==6) { 166 printf("%d\n",date[getsuc(root,x)]); 167 } 168 } 169 return 0; 170 }
本题用Treap的实现:引用自hzwer的博客
这是一道平衡树的模板题
treap是一棵修改了结点顺序的二叉查找树
通常树内的每个结点x都有一个关键字值key[x],另外,还要为结点分配一个随机数。
假设所有的优先级是不同的,所有的关键字也是不同的。treap的结点排列成让关键字遵循二叉查找树性质,并且优先级遵
循最小堆顺序性质:
1.如果v是u的左孩子,则key[v] < key[u].
2.如果v是u的右孩子,则key[v] > key[u].
3.如果v是u的孩子,则rand[u] > rand[u].
这两个性质的结合就是为什么这种树被称为“treap”的原因,因为它同时具有二叉查找树和heap的特征。
Treap代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<ctime> 4 using namespace std; 5 const int N=1e6+10; 6 struct tree{ 7 int l,r;//左右儿子节点编号 8 int num;//当前节点的数字 9 int s;//以当前节点为根的子树的节点数 10 int sum;//当前节点的数字的数量 11 int rnd;//随机优先级 12 }tr[N];//下标为节点编号 13 int n,rt,cnt,t1,t2; 14 void updata(int &k){ 15 int &l=tr[k].l,&r=tr[k].r; 16 tr[k].s=tr[l].s+tr[r].s+tr[k].sum; 17 } 18 void lturn(int &k){ 19 int t=tr[k].r;tr[k].r=tr[t].l;tr[t].l=k; 20 tr[t].s=tr[k].s;updata(k);k=t; 21 } 22 void rturn(int &k){ 23 int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k; 24 tr[t].s=tr[k].s;updata(k);k=t; 25 } 26 void insert(int &k,int x){ 27 if(!k){ 28 k=++cnt;tr[k].num=x;tr[k].s=1;tr[k].sum++;tr[k].rnd=rand();return ; 29 } 30 tr[k].s++; 31 int &l=tr[k].l,&r=tr[k].r; 32 if(x<tr[k].num){ 33 insert(l,x); 34 if(tr[l].rnd<tr[k].rnd) rturn(k); 35 } 36 else if(x>tr[k].num){ 37 insert(r,x); 38 if(tr[r].rnd<tr[k].rnd) lturn(k); 39 } 40 else{ 41 tr[k].sum++;return ; 42 } 43 } 44 void del(int &k,int x){ 45 if(!k) return ; 46 int &l=tr[k].l,&r=tr[k].r; 47 if(x==tr[k].num){ 48 if(tr[k].sum>1){ 49 tr[k].sum--;tr[k].s--;return ; 50 } 51 if(l*r==0) k=l+r; 52 else{ 53 if(tr[l].rnd<tr[r].rnd) rturn(k); 54 else lturn(k); 55 del(k,x); 56 } 57 } 58 else{ 59 tr[k].s--; 60 if(x>tr[k].num) del(r,x); 61 else del(l,x); 62 } 63 } 64 int find1(int &k,int x){ 65 if(!k) return 0; 66 int &l=tr[k].l,&r=tr[k].r; 67 if(tr[k].num==x) return tr[l].s+1; 68 if(tr[k].num>x) return find1(l,x); 69 if(tr[k].num<x) return tr[l].s+tr[k].sum+find1(r,x); 70 } 71 int find2(int &k,int x){ 72 if(!k) return 0; 73 int &l=tr[k].l,&r=tr[k].r; 74 if(tr[l].s+1<=x&&tr[l].s+tr[k].sum>=x) return tr[k].num; 75 if(tr[l].s>=x) return find2(l,x); 76 if(tr[l].s+tr[k].sum<x) return find2(r,x-tr[l].s-tr[k].sum); 77 } 78 void pred(int &k,int x){ 79 if(!k) return ; 80 int &l=tr[k].l,&r=tr[k].r; 81 if(tr[k].num<x){ 82 t1=tr[k].num; 83 pred(r,x); 84 } 85 else pred(l,x); 86 } 87 void succ(int &k,int x){ 88 if(!k) return ; 89 int &l=tr[k].l,&r=tr[k].r; 90 if(tr[k].num>x){ 91 t2=tr[k].num; 92 succ(l,x); 93 } 94 else succ(r,x); 95 } 96 int main(){ 97 srand(time(0)); 98 scanf("%d",&n); 99 for(int i=1,opt,x;i<=n;i++){ 100 scanf("%d%d",&opt,&x);t1=t2=0; 101 switch(opt){ 102 case 1:insert(rt,x);break; 103 case 2:del(rt,x);break; 104 case 3:printf("%d\n",find1(rt,x));break; 105 case 4:printf("%d\n",find2(rt,x));break; 106 case 5:pred(rt,x);printf("%d\n",t1);break; 107 case 6:succ(rt,x);printf("%d\n",t2);break; 108 } 109 } 110 return 0; 111 }
用vector暴力模拟可以AC,总耗时也就比正解慢100ms左右
vector代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<set> 5 #include<vector> 6 #include<algorithm> 7 #define inf 1000000000 8 using namespace std; 9 inline int read() 10 { 11 int x=0,f=1;char ch=getchar(); 12 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} 13 while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} 14 return x*f; 15 } 16 int n; 17 vector<int> a; 18 void insert(int x) 19 { 20 a.insert(upper_bound(a.begin(),a.end(),x),x); 21 return; 22 } 23 void del(int x) 24 { 25 a.erase(lower_bound(a.begin(),a.end(),x)); 26 return; 27 } 28 int find(int x) 29 { 30 return lower_bound(a.begin(),a.end(),x)-a.begin()+1; 31 } 32 int main() 33 { 34 n=read(); 35 a.reserve(200000); 36 int f,x; 37 for(int i=1;i<=n;i++) 38 { 39 f=read();x=read(); 40 switch(f) 41 { 42 case 1:insert(x);break; 43 case 2:del(x);break; 44 case 3:printf("%d\n",find(x));break; 45 case 4:printf("%d\n",a[x-1]);break; 46 case 5:printf("%d\n",*--lower_bound(a.begin(),a.end(),x));break; 47 case 6:printf("%d\n",*upper_bound(a.begin(),a.end(),x));break; 48 } 49 } 50 return 0; 51 }
用pbds中的红黑树的AC实现代码(竟然比vector的暴力模拟还慢上100ms,贼有意思!):
1 #include<bits/stdc++.h> 2 #include<ext/pb_ds/tree_policy.hpp> 3 #include<ext/pb_ds/assoc_container.hpp> 4 #define ll long long 5 #define il inline 6 using namespace std; 7 using namespace __gnu_pbds; 8 typedef tree<ll,null_type,less<ll>,rb_tree_tag,tree_order_statistics_node_update>t; 9 il int gi() 10 { 11 int a=0;char x=getchar();bool f=0; 12 while((x<‘0‘||x>‘9‘)&&x!=‘-‘)x=getchar(); 13 if(x==‘-‘)x=getchar(),f=1; 14 while(x>=‘0‘&&x<=‘9‘)a=a*10+x-48,x=getchar(); 15 return f?-a:a; 16 } 17 ll n,opt,x,ans; 18 int main() 19 { 20 t T; 21 n=gi(); 22 for(int i=1;i<=n;i++){ 23 opt=gi(),x=gi(); 24 if(opt==1)T.insert((x<<20)+i); 25 if(opt==2)T.erase(T.lower_bound(x<<20)); 26 if(opt==3)printf("%lld\n",T.order_of_key(x<<20)+1); 27 if(opt==4){ans=*T.find_by_order(x-1),printf("%lld\n",ans>>20);} 28 if(opt==5){ans=*--T.lower_bound(x<<20),printf("%lld\n",ans>>20);} 29 if(opt==6){ans=*T.upper_bound((x<<20)+n),printf("%lld\n",ans>>20);} 30 31 } 32 return 0; 33 }