[洛谷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 }//完璧な結末~
内容来自_ajthreac_的博客(https://www.cnblogs.com/juruoajh/),未经允许,不得转载。