树套树之线段树套平衡树学习笔记

总述:

  将平衡树的操作搬到区间上,如果没有修改操作,可以用主席树做做。如果有修改,主席树就难办了。这时可用线段树套平衡树。

  平时我们用几个变量维护线段树节点信息。操作复杂时,几个变量不能完成任务,可用更强大的平衡树维护线段树节点信息(树套树的本质)题目能用线段树做,那么题目问题定可以分解或转化成小问题(大区间可由若干小区间合并得到答案)

  套的平衡树依题而定,会影响常数。树套树常数很大(尤其在套splay的情况下)。

各操作实现参考大佬博客:浅谈树套树(线段树套平衡树)&学习笔记

例题:【模板】二逼平衡树(树套树)

个人AC代码(线段树套splay400行版)

  1 #include<iostream>
  2 #include<cstdio>
  3 
  4 #define qiuls (t<<1)
  5 #define qiurs ((t<<1)|1)
  6 #define qiumid (l+r>>1)
  7 
  8 using namespace std;
  9 
 10 const int N=5e4+5,FINF=-2147483647,INF=2147483647;
 11 
 12 int tre[N<<2],n,m,cnt,upp[N*56],ans;
 13 
 14 long long ai[N];
 15 
 16 struct node{
 17     int f,so[2],nu,w,siz;
 18 }s[N*56];
 19 
 20 inline long long read()//ll N
 21 {
 22     long long x=0;
 23     bool f=0;
 24     char ch=getchar();
 25     while(!isdigit(ch))
 26         f|=ch=='-',ch=getchar();
 27     while(isdigit(ch))
 28         x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
 29     return  f?-x:x;
 30 }
 31 
 32 inline int newnode(long long v)
 33 {
 34     s[++cnt].nu=v;
 35     s[cnt].siz=s[cnt].w=1;
 36     return cnt;
 37 }
 38 
 39 inline void rotate(int t)
 40 {
 41     int u=s[t].f;
 42     if(!u)
 43         return;
 44     int ff=s[u].f;
 45     if(upp[u])
 46     {
 47         upp[t]=upp[u];
 48         tre[upp[t]]=t;
 49         upp[u]=0;
 50     }
 51     if(ff)
 52         s[ff].so[s[ff].so[1]==u]=t;
 53     int st=s[u].so[1]==t;
 54     int v=s[t].so[!st];
 55     s[t].f=ff;
 56     s[u].f=t;
 57     s[u].so[st]=v;
 58     if(v)
 59         s[v].f=u;
 60     s[t].so[!st]=u;
 61     s[u].siz=s[s[u].so[0]].siz+s[s[u].so[1]].siz+s[u].w;
 62     s[t].siz=s[s[t].so[0]].siz+s[s[t].so[1]].siz+s[t].w;
 63 }
 64 
 65 void splay(int t,int ff)
 66 {
 67     int u,v;
 68     while(s[t].f!=ff)
 69     {
 70         u=s[t].f;
 71         if(s[u].f==ff)
 72         {
 73             rotate(t);
 74             return;
 75         }
 76         else
 77             v=s[u].f;
 78         if((s[u].so[1]==t)==(s[v].so[1]==u))
 79             rotate(u),rotate(t);
 80         else
 81             rotate(t),rotate(t);
 82     }
 83 }
 84 
 85 void sinsert(int t,int v)
 86 {
 87     s[t].siz++;
 88     if(v==s[t].nu)
 89     {
 90         s[t].w++;
 91         splay(t,0);
 92         return;
 93     }
 94     if(v<s[t].nu)
 95     {
 96         if(s[t].so[0])
 97             sinsert(s[t].so[0],v);
 98         else
 99         {
100             s[t].so[0]=newnode(v);
101             s[cnt].f=t;
102             splay(cnt,0);
103         }
104     }
105     else
106     {
107         if(s[t].so[1])
108             sinsert(s[t].so[1],v);
109         else
110         {
111             s[t].so[1]=newnode(v);
112             s[cnt].f=t;
113             splay(cnt,0);
114         }
115     }
116 }
117 
118 void build(int t,int l,int r)//有虚点 
119 {
120     tre[t]=++cnt;
121     upp[cnt]=t;
122     s[cnt].nu=FINF;
123     s[cnt].siz=s[cnt].w=1;
124     sinsert(cnt,INF);
125     for(int i=l;i<=r;++i)
126         sinsert(tre[t],ai[i]);
127     if(l==r)
128         return;
129     build(qiuls,l,qiumid);
130     build(qiurs,qiumid+1,r);
131 }
132 
133 int sfinrank(int t,long long k)
134 {
135     if(!t)
136         return 0;
137     if(k==s[t].nu)
138     {
139         int ret=s[s[t].so[0]].siz;
140         splay(t,0);
141         return ret;
142     }
143     if(k<s[t].nu)
144     {
145         if(s[t].so[0])
146             return sfinrank(s[t].so[0],k);
147         else
148         {
149             splay(t,0);
150             return 0;
151         }
152     }
153     else
154     {
155         if(s[t].so[1])
156             return s[s[t].so[0]].siz+s[t].w+sfinrank(s[t].so[1],k);
157         else
158         {
159             int ret=s[s[t].so[0]].siz+s[t].w;
160             splay(t,0);
161             return ret;
162         }
163     }
164 }
165 
166 void finrank(int t,int l,int r,int ll,int rr,long long k)//多少数小于k 
167 {
168     if(ll<=l&&r<=rr)
169     {
170         ans+=sfinrank(tre[t],k)-1;
171         return;
172     }
173     int mid=qiumid;
174     if(ll<=mid)
175     {
176         if(rr>mid)
177             finrank(qiuls,l,mid,ll,rr,k),finrank(qiurs,mid+1,r,ll,rr,k);
178         else
179             finrank(qiuls,l,mid,ll,rr,k);
180     }
181     else
182         finrank(qiurs,mid+1,r,ll,rr,k);
183 }
184 
185 void sdel(int t,long long k)
186 {
187     s[t].siz--;
188     if(!t)
189     {
190         cout<<"****";
191         return;
192     } 
193     if(k==s[t].nu)
194     {
195         if(s[t].w>1)
196         {
197             s[t].w--;
198             splay(t,0);
199             return;
200         }
201         if(s[t].so[0]&&s[t].so[1])
202         {
203             int u=s[t].so[0];
204             while(s[u].so[1])
205                 u=s[u].so[1];
206             if(upp[t])
207             {
208                 upp[u]=upp[t];
209                 tre[upp[u]]=u;
210             }
211             s[s[t].f].so[s[s[t].f].so[1]==t]=u;    
212             s[s[t].so[1]].f=u;
213             if(u==s[t].so[0])
214             {
215                 s[u].f=s[t].f;s[u].siz=s[t].siz;
216                 s[u].so[1]=s[t].so[1];
217                 splay(u,0);
218             }
219             else
220             {
221                 int v=s[t].so[0];
222                 while(v!=u)
223                 {
224                     s[v].siz--;
225                     v=s[v].so[1];
226                 }
227                 if(s[u].so[0])
228                     s[s[u].so[0]].f=t;
229                 s[s[t].so[0]].f=u;
230                 s[s[u].f].so[1]=t;
231                 swap(s[u].f,s[t].f);
232                 swap(s[u].so,s[t].so);
233                 swap(s[u].siz,s[t].siz);
234                 sdel(t,k);
235             }
236         }
237         else
238         {
239             int u=s[t].so[0]+s[t].so[1];
240             s[s[t].f].so[s[s[t].f].so[1]==t]=u;
241             if(u)
242                 s[u].f=s[t].f;
243             splay(s[t].f,0);
244         }
245     }
246     else
247     {
248         if(k<s[t].nu)
249             sdel(s[t].so[0],k);
250         else
251             sdel(s[t].so[1],k);
252     }
253 }
254 
255 void modify(int t,int l,int r,int ll,long long k)
256 {
257             //注意splay变空的情况
258             //更新,因为有虚点,所以不会变空 
259     sdel(tre[t],ai[ll]);
260     sinsert(tre[t],k);
261     if(l==r)
262         return;
263     int mid=qiumid;
264     if(ll<=mid)
265         modify(qiuls,l,mid,ll,k);
266     else
267         modify(qiurs,mid+1,r,ll,k);
268 }
269 
270 void sfinpre(int t,long long k)
271 {
272     if(!t)
273         return;
274     if(s[t].nu<k)
275     {
276         ans=max(ans,s[t].nu);
277         if(s[t].so[1])
278             sfinpre(s[t].so[1],k);
279         else
280             splay(t,0);
281     }
282     else
283         if(s[t].so[0])
284             sfinpre(s[t].so[0],k);
285         else
286             splay(t,0);
287 }
288 
289 void sfinnxt(int t,long long k)
290 {
291     if(!t)
292         return;
293     if(s[t].nu>k)
294     {
295         ans=min(ans,s[t].nu);
296         if(s[t].so[0])
297             sfinnxt(s[t].so[0],k);
298         else
299             splay(t,0);
300     }
301     else
302         if(s[t].so[1])
303             sfinnxt(s[t].so[1],k);
304         else
305             splay(t,0);
306 }
307 
308 void finpre(int t,int l,int r,int ll,int rr,long long k)
309 {
310     if(ll<=l&&r<=rr)
311     {
312         sfinpre(tre[t],k);
313         return;
314     }
315     int mid=qiumid;
316     if(ll<=qiumid)
317     {
318         if(rr>qiumid)
319             finpre(qiuls,l,mid,ll,rr,k),finpre(qiurs,mid+1,r,ll,rr,k);
320         else
321             finpre(qiuls,l,mid,ll,rr,k);
322     }
323     else
324         finpre(qiurs,mid+1,r,ll,rr,k);
325 }
326 
327 void finnxt(int t,int l,int r,int ll,int rr,long long k)
328 {
329     if(ll<=l&&r<=rr)
330     {
331         sfinnxt(tre[t],k);
332         return;
333     }
334     int mid=qiumid;
335     if(ll<=qiumid)
336     {
337         if(rr>qiumid)
338             finnxt(qiuls,l,mid,ll,rr,k),finnxt(qiurs,mid+1,r,ll,rr,k);
339         else
340             finnxt(qiuls,l,mid,ll,rr,k);
341     }
342     else
343         finnxt(qiurs,mid+1,r,ll,rr,k);
344 }
345 
346 int main()
347 {
348     n=read(),m=read();
349     for(int i=1;i<=n;++i)
350         ai[i]=read();
351     build(1,1,n);
352     int opt,l,r;
353     long long k;
354     int ll,rr,mid;
355     for(int i=1;i<=m;++i)
356     {
357         opt=read();
358         switch(opt)
359         {
360             case 1:
361                 l=read(),r=read(),k=read();
362                 ans=0;
363                 finrank(1,1,n,l,r,k);
364                 printf("%d\n",ans+1);
365                 break;
366             case 2:
367                 l=read(),r=read(),k=read();
368                 ll=0,rr=1e8;
369                 while(ll<rr)
370                 {
371                     mid=(ll+rr)>>1;
372                     ans=0;
373                     finrank(1,1,n,l,r,mid);
374                     if(ans<k)
375                         ll=mid+1;
376                     else
377                         rr=mid;
378                 }
379                 printf("%d\n",ll-1);
380                 break;
381             case 3:
382                 l=read(),k=read();
383                 modify(1,1,n,l,k);
384                 ai[l]=k;
385                 break;
386             case 4:
387                 l=read(),r=read(),k=read();
388                 if(k<=0)
389                 {
390                     printf("%d\n",FINF);
391                     break;
392                 }
393                 ans=-INF;
394                 finpre(1,1,n,l,r,k);
395                 printf("%d\n",ans);
396                 break;
397             case 5:
398                 l=read(),r=read(),k=read();
399                 if(k>=1e8)
400                 {
401                     printf("%d\n",INF);
402                     break;
403                 }
404                 ans=INF;
405                 finnxt(1,1,n,l,r,k);
406                 printf("%d\n",ans);
407                 break;
408         }
409     }
410     return 0;
411 }
霸屏警告

代码中给每个线段树的节点上的splay都多加了两个虚点-INF和INF 一能防止modify的删除节点时使splay变空的情况,二能防止没有前驱/后缀的情况。后来想一想,发现实际上对于情况1,先加后删就行;情况二:因为用的全局变量ans取遇见的最优值,只要ans初值设好,也不怕没前驱/后继。总之,虚点不用加了,加了还得考虑对sfinrank的影响(减一),画蛇添足。

几个注意点:

  1、内存:线段树为4N*int,所有splay的总内存:

    (2(虚点内存。不用虚点 可不加2) + (ceil(logn)+1)(线段树层数))*N+M* (ceil(logn)+1) *node (node为splay的节点结构体,存一个节点的全部信息)

     不算虚点的话,splay的总内存为: (ceil(logn)+1)(N+M)*node

  2、时间:上限复杂度:O(n log^3 n) 查排名为k的数单次操作复杂度为log^3 n

      (听说用树状数组套主席树可以压到log^2 n ?)

  3、平衡树中点的位置变动时,要注意维护:该点及其周围有联系的所有点的所有信息。别忘了该点被线段树上的点直接指向的情况。

  4、用到splay的话,注意每次进入splay操作后都要进行一次'splay操作',否则能被出题人造特殊数据卡;要在返回的答案(例如s[s[t].so[0]].siz+s[t].w等)记录下之后再splay操作,因为splay操作会改变splay的结构,即splay操作后,s[s[t].so[0]].siz+s[t].w很可能会变。

  5、树套树代码量极大、调试难度高,不推荐解题时首选该方法。(众所周知树套树的题都能不用树套树做 \~逃)

    常常被分块取代。可离线的话会被其他优秀算法(例如整体二分、cdq分治 (可惜现在都不会 \捂脸)))各种暴打。

扩展应用:

  1、区间修改:标记永久化

posted @ 2020-11-26 16:01  千叶繁华  阅读(285)  评论(0编辑  收藏  举报