[模板]洛谷T3380 二逼平衡树 线段树套Splay
Posted Running-Coder
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[模板]洛谷T3380 二逼平衡树 线段树套Splay相关的知识,希望对你有一定的参考价值。
时隔一个月后,终于攻克了二分求K大的难题。。。特此纪念 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 }
以上是关于[模板]洛谷T3380 二逼平衡树 线段树套Splay的主要内容,如果未能解决你的问题,请参考以下文章