[洛谷P3850][题解][TJOI2007]书架

题目戳这里

平衡树的题第一次一遍过,写篇博客庆祝一下,顺便加深一下印象

这道题要求我们支持任意插入

可以用Splay做,具体做法如下:

设要插入到第k个节点后,

则先将k旋至根,再将k-1旋至k的左儿子,

可以发现,此时k-1的右儿子是空的,我们将新节点插在这里就可以满足题意

Code:

  1 #include<bits/stdc++.h>
  2 #define INF 0x3f3f3f3f
  3 #define ls ch[k][0]
  4 #define rs ch[k][1]
  5 #define N 1000010
  6 using namespace std;
  7 inline void Read(int &x){
  8     int f=1;
  9     char c=getchar();
 10     x=0;
 11     while(c<'0'||c>'9'){
 12         if(c=='-')f=-1;
 13         c=getchar();
 14     }
 15     while(c>='0'&&c<='9'){
 16         x=(x<<3)+(x<<1)+c-'0';
 17         c=getchar();
 18     }
 19     x*=f;
 20 }
 21 
 22 /*变量名解释:
 23 n,m,q:如题意 
 24 tot:当前点数,root:当前根 
 25 ch[i][0/1]:i的左/右儿子,fa[i]:i的父亲 
 26 data[i]:i对应的编号,cnt[i]:i有几个,siz[i]:i的子树大小 
 27 bks[i]:第i个字符串(书名)*/ 
 28 int n,m,q,tot,root;
 29 int ch[N][2],fa[N],data[N],cnt[N],siz[N];
 30 string bks[N];
 31 
 32 inline int Son(int k){//k是他爹的哪个儿子 
 33     return ch[fa[k]][1]==k;
 34 }
 35 
 36 inline void Pushup(int k){//向上更新 
 37     siz[k]=siz[ls]+siz[rs]+cnt[k];
 38 }
 39 
 40 inline void Rotate(int k){//旋转 
 41     int fk=fa[k],gfk=fa[fk];
 42     int b=Son(k),c=Son(fk),a=ch[k][!b];
 43     ch[fk][b]=a,fa[a]=fk;//
 44     ch[gfk][c]=k,fa[k]=gfk;//
 45     ch[k][!b]=fk,fa[fk]=k;//
 46     Pushup(fk),Pushup(k);
 47 }
 48 
 49 inline void Splay(int k,int to){//将k伸展至to 
 50     while(fa[k]!=to){
 51         int fk=fa[k],gfk=fa[fk];
 52         if(gfk!=to){
 53             if(Son(k)==Son(fk))Rotate(fk);
 54             else Rotate(k);
 55         }
 56         Rotate(k);
 57     }
 58     if(!to)root=k;
 59 }
 60 
 61 inline void Find(int num){//把值为num的节点搞到根 
 62     if(!root)return;
 63     int k=root;
 64     while(data[ch[k][num>data[k]]]&&num!=data[k]){
 65         k=ch[k][num>data[k]];
 66     }
 67     Splay(k,0);
 68 }
 69 
 70 inline void Insert(int num){//插入值为num的节点 
 71     int k=root,tmp=0;
 72     while(k&&data[k]!=num){//
 73         tmp=k;
 74         k=ch[k][num>data[k]];
 75     }
 76     if(k)cnt[k]++;//有了,直接记录 
 77     else {//还没有,需要新增 
 78         k=++tot;
 79         if(tmp)ch[tmp][num>data[tmp]]=k;
 80         ls=rs=0;
 81         data[k]=num,fa[k]=tmp;
 82         cnt[k]=siz[k]=1;
 83     }
 84     Splay(k,0);
 85 }
 86 
 87 inline int GetRank(int num){//查询num的排名 
 88     Find(num);
 89     return siz[ch[root][0]]+1;
 90 }
 91 
 92 inline int GetKth(int num){//查询第num大 
 93     int k=root;
 94     while(114514){
 95         if(ls&&siz[ls]>=num)k=ls;
 96         else {
 97             if(siz[ls]+cnt[k]<num){
 98                 num-=(siz[ls]+cnt[k]);
 99                 k=rs;
100             }else return k;
101         }
102     }
103 }
104 
105 inline int GetPre(int num){//找前驱 
106     Find(num);
107     if(data[root]<num)return root;
108     int k=ch[root][0];
109     while(rs)k=rs;
110     return k;
111 }
112 
113 inline int GetSuc(int num){//找后继 
114     Find(num);
115     if(data[root]>num)return root;
116     int k=ch[root][1];
117     while(ls)k=ls;
118     return k;
119 }
120 
121 inline void Delete(int num){//删节点 
122     int lst=GetPre(num),nxt=GetSuc(num);
123     Splay(lst,0),Splay(nxt,lst);
124     //找到前驱后缀,中间那个拎出来处决 
125     int victim=ch[nxt][0];
126     if(cnt[victim]>1){
127         cnt[victim]--;
128         Splay(victim,0);
129     }else {
130         ch[nxt][0]=0;
131         Splay(nxt,0);
132     }
133 }
134 
135 int main(){
136     Read(n);
137     Insert(-INF),Insert(INF);//防止一些奇奇怪怪的毛病 
138     for(int i=1;i<=n;i++){
139         cin>>bks[i];
140         Insert(i);
141     }
142     Read(m);
143     for(int i=1;i<=m;i++){
144         int k;
145         cin>>bks[n+i];
146         Read(k);
147         k+=2;
148         //k旋到根,k-1(k的前驱)旋到k左边
149         //此时k-1右边无节点,插到这里即可 
150         //找+旋 
151         int kth=GetKth(k);
152         Splay(kth,0);
153         int sucth=GetKth(k-1);
154         Splay(sucth,kth);
155         //添加节点 
156         ch[sucth][1]=++tot,fa[tot]=sucth;
157         ch[tot][0]=ch[tot][1]=0;
158         data[tot]=k,cnt[tot]=siz[tot]=1;
159         Pushup(sucth),Pushup(kth);
160     }
161     Read(q);
162     for(int i=1;i<=q;i++){
163         int k;
164         Read(k);
165         int kth=GetKth(k+2);
166         cout<<bks[kth-2]<<endl;
167     }
168     return 0;
169 }//完璧な結末~ 
内容来自_ajhfff_的博客(https://www.cnblogs.com/juruoajh/),未经允许,不得转载。
原文地址:https://www.cnblogs.com/juruoajh/p/12304975.html