[模板]洛谷T3380 二逼平衡树 线段树套BST

时隔一个月后,终于攻克了二分求K大的难题。。。特此纪念 2017.10.3

好好的树套树模板题怎么没有一份线段树套平衡树的“标准”题解呢。。。虽然这方法比较low,但我还是来贴一发。。。

据说这题常数巨大,若干份线段树套BST的代码都被卡常了

萌新我看到这吓人的通过率不由瑟瑟发抖

于是使出浑身解数,用了一大堆奇技淫巧进行疯狂的常数优化。。。

然后。。。居然没被卡到。。。滑稽。。。

进入正题:

何为树套树?

考虑这题要求的操作。区间操作要求使用线段树,求排名、K大什么的又要求使用BST,所以就可以祭出线段树套BST了,具体是指,在线段树划定的每个区间上都分别建立一棵BST。这样,空间复杂度就是线段树的宽×高,即为nlogn。

这样操作,就将原先的一段区间分成了logn棵BST。对每个BST操作后,将结果合并即得答案。显然,这样一次操作的时间复杂度是log^2n。

操作解析:

每次操作先由线段树自顶向下查询,若当前线段树区间完全包含于待操作区间,则在本区间所属的BST内进行相应的平衡树操作并返回结果,全部操作结束后将所有结果合并即可。

对于操作1:在每个BST内查询严格小于k的数的个数,将所有结果相加再加1即为答案。

对于操作2:

先吐槽一句。。。就是这个操作,折磨了我一个月之久。。。二分的细节什么的太恶心。。。

对于树套树,这个操作貌似是无法直接实现的,因为区间并不是有序的QwQ

那么要怎么搞呢?答案是二分,每次对二分出的值查询一次区间排名,不断逼近给定的k即可。

但是,由于元素重复等等的原因,使得这个二分过程难以实现。。。

这里对代码中的二分细节作一下解释:

1.x的计算紧跟在l和r的变动之后,目的是使这三个值保持同步,保证结果准确;

2.如果x的排名>k,联系求排名操作的性质,可推知这时的x一定大于结果,那么就将x以及比x大的值都排除,故有r=x-1。通过类似推理可得,当x的排名<=k时,须执行l=x;

3.x的计算问题。

如果写成x=(l+r)>>1,那么在某些情况下会死循环。原因如下:

当l+1==r且对l查询满足其排名<=k时,按照(l+r)>>1计算的x等于l,那么会导致执行l=x,然而原本就有l==x。。。于是就死循环了~

写成x=(l+r+1)>>1的作用是,让x偏向于r,这样,在发生上述情况时,会有如下变动:

x=(l+r+1)>>1=r,当x的排名>k,那么执行r=x-1=l;当x的排名<=k,那么执行l=x=r。无论如何,都将得到l==r而退出循环,而不是死循环。

如此便能解决问题。

对于操作3:首先在原序列中找到pos位置原先的值,然后在每个BST中将该值删除再插入k即可。注意也要修改原序列中的值,以便下一次操作3时使用。

对于操作4:在每个BST内求一次前驱,最后对所有结果取一次Max即可。

对于操作5:与操作4同理。

下面讲一下我的“奇技淫巧”。。。

对于线段树套BST,因为线段树的结构稳定,所以BST就在很大程度上决定了代码的性能如何。

那么选择怎样的BST呢?每次都进行暴力单点插入?不行不行,会被卡常。。。

NOI2005 维护数列 这道题告诉我,建立完全平衡的BST,其性能远优于上面的方法。这样做,不仅建树速度快,而且对于提升后续操作的性能效果明显。

但是,建立完全平衡的BST,需要序列有序,然而这里的序列是无序的。

每次都排序一下?不行不行,时间更会炸掉。。。

这时我想,能不能让当前排序后的序列,方便上层序列的排序,从而减少时间消耗呢?

突然我回想起归并排序的性质。。。没错!对于建树的过程,整体嵌入一个归并,便可达到目的,建立起完全平衡的BST。

奇技淫巧”细节详见代码。

代码如下:

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<ctime>
  6 #include<cstdlib>
  7 
  8 #include<string>
  9 #include<stack>
 10 #include<queue>
 11 #include<vector>
 12 #include<algorithm>
 13 #include<map>
 14 #include<set>
 15 
 16 #define inf 2147483647
 17 
 18 using namespace std;
 19 
 20 inline void read(int &x){  //读入优化 
 21     x=0;
 22     char t=getchar();
 23     bool f=0;
 24     
 25     while(t<'0' || t>'9'){
 26         if(t=='-')f=1;
 27         t=getchar();
 28     }
 29     
 30     while(t>='0' && t<='9'){
 31         x=(x<<3)+(x<<1)+t-'0';
 32         t=getchar();
 33     }
 34     
 35     if(f)x=-x;
 36 }
 37 
 38 struct xb{  //合并重复后的待建节点 
 39     int v,num;
 40 };
 41 
 42 int c[100010];  //原序列
 43 xb yby[100010];  //合并后的待建序列 
 44 int temp[100010];  //排序用辅助序列
 45 int longtao[100010];  //归并用辅助序列
 46 
 47 int rank_ans;
 48 int pre_ans,succ_ans;
 49 int k,l,r;
 50 
 51 int n,m,i;
 52 int opt,a,b,pos,x;
 53 
 54 void merge(int,int,int);  //归并 
 55 
 56 struct node{  //平衡树节点定义 
 57     int key;
 58     int size,num;
 59     node *ch[2];
 60     
 61     void make(int x){  //新建节点,x为下标,指向待建节点 
 62         key=yby[x].v;
 63         size=num=yby[x].num;
 64         ch[0]=ch[1]=NULL;
 65     }
 66     
 67     void maintain(){
 68         size=num;
 69         if(ch[0]!=NULL)size+=ch[0]->size;
 70         if(ch[1]!=NULL)size+=ch[1]->size;
 71     }
 72 };
 73 
 74 void rotate(node* &,bool);  //旋转 
 75 void insert(node* &,int);  //插入 
 76 void del(node* &,int);  //删除 
 77 void unit(int,int,node* &);  //合并待建序列中的重复元素 
 78 void bst_build(node* &,int,int,int);  //平衡树建树 
 79 int bst_rank(node *,int);  //求排名 
 80 void bst_pre(node *,int);  //求前驱 
 81 void bst_succ(node *,int);  //求后继 
 82 
 83 struct sgt{  //线段树 
 84     node *p[200010];
 85     
 86     void build(int o,int l,int r){
 87         int mid=(l+r)>>1;
 88         
 89         if(l<r){
 90             int lson=o<<1;
 91             int rson=lson|1;
 92             
 93             build(lson,l,mid);
 94             build(rson,mid+1,r);
 95         }
 96         
 97         merge(l,r,mid);
 98         
 99         p[o]=NULL;
100         unit(l,r,p[o]);
101     }
102     
103     void rank(int o,int l,int r){
104         if(l>=a && r<=b)rank_ans+=bst_rank(p[o],x)-1;
105         
106         else{
107             int mid=(l+r)>>1;
108             int lson=o<<1;
109             int rson=lson|1;
110             
111             if(a<=mid)rank(lson,l,mid);
112             if(b>mid)rank(rson,mid+1,r);
113         }
114     }
115     
116     void update(int o,int l,int r){
117         del(p[o],c[pos]);
118         insert(p[o],x);
119         
120         if(l<r){
121             int mid=(l+r)>>1;
122             int lson=o<<1;
123             int rson=lson|1;
124             
125             if(pos<=mid)update(lson,l,mid);
126             else update(rson,mid+1,r);
127         }
128     }
129     
130     void pre(int o,int l,int r){
131         if(l>=a && r<=b)bst_pre(p[o],x);
132         
133         else{
134             int mid=(l+r)>>1;
135             int lson=o<<1;
136             int rson=lson|1;
137             
138             if(a<=mid)pre(lson,l,mid);
139             if(b>mid)pre(rson,mid+1,r);
140         }
141     }
142     
143     void succ(int o,int l,int r){
144         if(l>=a && r<=b)bst_succ(p[o],x);
145         
146         else{
147             int mid=(l+r)>>1;
148             int lson=o<<1;
149             int rson=lson|1;
150             
151             if(a<=mid)succ(lson,l,mid);
152             if(b>mid)succ(rson,mid+1,r);
153         }
154     }
155 } tree;
156 
157 int main(){
158     srand(time(0));
159     
160     read(n);read(m);
161     
162     for(i=1;i<=n;i++){
163         read(c[i]);
164         temp[i]=c[i];
165     }
166     
167     tree.build(1,1,n);
168     
169     for(i=1;i<=m;i++){
170         read(opt);
171         
172         switch(opt){
173             case 1:{
174                 read(a);read(b);read(x);
175                 
176                 rank_ans=1;
177                 tree.rank(1,1,n);
178                 printf("%d\n",rank_ans);
179                 break;
180             }
181             
182             case 2:{
183                 read(a);read(b);read(k);
184                 
185                 l=0;
186                 r=100000000;
187                 x=(l+r+1)>>1;
188                 
189                 while(l<r){
190                     rank_ans=1;
191                     tree.rank(1,1,n);
192                     
193                     if(rank_ans>k)r=x-1;
194                     else l=x;
195                     
196                     x=(l+r+1)>>1;
197                 }
198                 
199                 printf("%d\n",x);
200                 break;
201             }
202             
203             case 3:{
204                 read(pos);read(x);
205                 
206                 tree.update(1,1,n);
207                 c[pos]=x;
208                 break;
209             }
210             
211             case 4:{
212                 read(a);read(b);read(x);
213                 
214                 pre_ans=-inf;
215                 tree.pre(1,1,n);
216                 printf("%d\n",pre_ans);
217                 break;
218             }
219             
220             case 5:{
221                 read(a);read(b);read(x);
222                 
223                 succ_ans=inf;
224                 tree.succ(1,1,n);
225                 printf("%d\n",succ_ans);
226                 break;
227             }
228         }
229     }
230     
231     return 0;
232 } 
233 
234 void rotate(node* &p,bool f){
235     node *t=p->ch[f^1];
236     p->ch[f^1]=t->ch[f];
237     t->ch[f]=p;
238     
239     p->maintain();
240     t->maintain();
241     
242     p=t;
243 }
244 
245 void insert(node* &p,int x){
246     if(p==NULL){
247         p=(node *)malloc(sizeof(node));
248         p->key=x;
249         p->size=p->num=1;
250         p->ch[0]=p->ch[1]=NULL;
251         return;
252     }
253     
254     if(x==p->key){
255         p->size++;
256         p->num++;
257         return;
258     }
259     
260     if(x<p->key){
261         insert(p->ch[0],x);
262         p->size++;
263     }
264     else{
265         insert(p->ch[1],x);
266         p->size++;
267     }
268 }
269 
270 void del(node* &p,int x){
271     if(p==NULL)return;
272     
273     if(x==p->key){
274         if(p->num>1){
275             p->num--;
276             p->size--;
277             return;
278         }
279         else{
280             if(p->ch[0]==NULL){
281                 node* t=p;
282                 p=p->ch[1];
283                 free(t);
284                 return;
285             }
286             else if(p->ch[1]==NULL){
287                 node* t=p;
288                 p=p->ch[0];
289                 free(t);
290                 return;
291             }
292             else{
293                 bool f=rand()&1;
294                 rotate(p,f);
295                 del(p->ch[f],x);
296                 p->size--;
297             }
298         }
299     }
300     else{
301         if(x<p->key)del(p->ch[0],x);
302         else del(p->ch[1],x);
303         p->size--;
304     }
305 }
306 
307 void unit(int l,int r,node* &root){
308     int i;
309     int p_yby=1;
310     
311     yby[1].v=temp[l];
312     yby[1].num=1;
313     
314     for(i=l+1;i<=r;i++){
315         if(temp[i]==yby[p_yby].v)yby[p_yby].num++;
316         else{
317             p_yby++;
318             yby[p_yby].v=temp[i];
319             yby[p_yby].num=1;
320         }
321     }
322     
323     bst_build(root,1,p_yby,(1+p_yby)>>1);
324 }
325 
326 void bst_build(node* &p,int l,int r,int mid){
327     p=(node *)malloc(sizeof(node));
328     p->make(mid);
329     
330     if(mid-1>=l)bst_build(p->ch[0],l,mid-1,(l+mid-1)>>1);
331     if(mid+1<=r)bst_build(p->ch[1],mid+1,r,(mid+1+r)>>1);
332     
333     p->maintain();
334 }
335 
336 int bst_rank(node *p,int x){
337     if(p==NULL)return 1;
338     
339     int s=0;
340     if(p->ch[0]!=NULL)s=p->ch[0]->size;
341     
342     if(x<p->key)return bst_rank(p->ch[0],x);
343     else if(x==p->key)return s+1;
344     else return s+p->num+bst_rank(p->ch[1],x);
345 }
346 
347 void merge(int head,int tail,int mid){
348     if(head==tail)return;
349     
350     int p1=head,p2=mid+1,ph=head;
351     
352     while(p1<=mid && p2<=tail){
353         if(temp[p1]<temp[p2]){
354             longtao[ph]=temp[p1];
355             p1++;
356             ph++;
357         }
358         else{
359             longtao[ph]=temp[p2];
360             p2++;
361             ph++;
362         }
363     }
364     
365     while(p1<=mid){
366         longtao[ph]=temp[p1];
367         p1++;
368         ph++;
369     }
370     
371     while(p2<=tail){
372         longtao[ph]=temp[p2];
373         p2++;
374         ph++;
375     }
376     
377     for(i=head;i<=tail;i++)temp[i]=longtao[i];
378 }
379 
380 void bst_pre(node *p,int x){
381     if(p==NULL)return;
382     if(p->key<x){
383         pre_ans=max(pre_ans,p->key);
384         bst_pre(p->ch[1],x);
385     }
386     else bst_pre(p->ch[0],x);
387 }
388 
389 void bst_succ(node *p,int x){
390     if(p==NULL)return;
391     if(p->key>x){
392         succ_ans=min(succ_ans,p->key);
393         bst_succ(p->ch[0],x);
394     }
395     else bst_succ(p->ch[1],x);
396 }

转载于:https://www.cnblogs.com/running-coder-wfh/p/7624454.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值