文艺平衡树-splay的区间操作

  真的是个神题,蒟蒻表示无力吐槽。刚开始以为是一个板子题,看着题解打了一遍,大概也理解了他是怎么实现的,然后我就去做别的题了,然后就在Three_D大佬的询问下蒙*了。最后还是问的nc哥,并思考了一个中午才搞明白。最主要的一点是,旋转不会改变树的中序遍历。

【建树操作】

  对于一棵BST,区间[l,r],如果把l-1 splay到根,把r+1 splay到根的右子树,那么[l,r]即为根的右子树的左子树,如果不是BST,这个性质同样适用。然后因为旋转可能会涉及到1n,所以要建立1n+2两个哨兵节点;因为平衡树维护的是一个序列,所以一开始树的中序遍历应该是原序列:

 1 rt=build_tree(0,1,n+2);
 2 int build_tree(int fa,int l,int r)
 3 {
 4     if(l>r) return 0;
 5     int mid=(l+r)>>1,
 6         now=++sz;
 7     key[now]=data[mid];f[now]=fa;tag[now]=0;
 8     ch[now][0]=build_tree(now,l,mid-1);
 9     ch[now][1]=build_tree(now,mid+1,r);
10     pushup(now);
11     return now;
12 }
View Code

 

splay

  rotate不改变树的中序遍历,原rotate函数不变。因为要将r+2 splay到根的右节点而不是根,所以要多传一个参:

1 void splay(int x,int goal)
2 {
3     for(int fa;(fa=f[x])!=goal;rotate(x))
4         if(f[fa]!=goal)        
5             rotate(get(fa)==get(x)?fa:x);
6     if(!goal)rt=x;
7 }
View Code

 

【翻转区间】*

  对于区间[l,r],l splay到根,r+2 splay到根的右儿子,那么r+2的左子树就是[l+1,r+1].如果将r+2的左儿子的左右子树翻转,并递归地翻转下去,那么[l+1r+1]的中序遍历就会翻转。但是这样的时间复杂度太高,所以延续线段树中懒标记的操作。

1 void turn(int l,int r)
2 {
3     l=rnk(l);
4     r=rnk(r+2);
5     splay(l,0);
6     splay(r,l);
7     pushdown(rt);
8     tag[ch[ch[rt][1]][0]]^=1;
9 }
View Code

  然后说说把我弄懵逼的东西,对于序列 [12345],平衡树如图:

  绿色的数字为key值,一定要注意区分节点的排名和这个节点的值,在建树时,key[now]=data[mid];当前节点的key存储的是序列的值,而图中黑色的数字是节点的排名,可能有点难以理解,那举个例子:

  对于[12345],如果要翻转区间[2,3],那么在平衡树中找到排名为2的数(l=rnk(l)),即2,将他旋转到根,找到排名为5的数(r=rnk(r+2)),即5,将他旋转到根的右儿子,则5的左子树为[34],key值就是所要翻转的区间[23],打上标记,翻转完成。

Ps.除区间翻转外,还可以进行区间删除,区间加上x等操作。

标记下传】

  每到一个节点就要下传懒标记:

 1 void pushdown(int x)
 2 {
 3     if(x && tag[x])
 4     {
 5         tag[ch[x][0]]=tag[ch[x][0]]^1;
 6         tag[ch[x][1]]=tag[ch[x][1]]^1;
 7         swap(ch[x][0],ch[x][1]);
 8         tag[x]=0;
 9     }
10 }
View Code

 

【输出结果】

  中序遍历整棵树即可:

1 void write(int now)
2 {
3     pushdown(now);
4     if(ch[now][0])write(ch[now][0]);
5     if(key[now]!=INF && key[now]!=-INF)cout<<key[now]<<" ";
6     if(ch[now][1]) write(ch[now][1]);
7 }
View Code

 

【完整代码

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #define INF 0x7fffffff
 5 using namespace std;
 6 int n,m,data[1000000];
 7 int ch[1000000][2],size[1000000],cnt[1000000],f[1000000],key[1000000],tag[1000000],sz,rt;
 8 
 9 int get(int x){return ch[f[x]][1]==x;}
10 void pushup(int x)
11 {
12     size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
13 }
14 void pushdown(int x)
15 {
16     if(x && tag[x])
17     {
18         tag[ch[x][0]]=tag[ch[x][0]]^1;
19         tag[ch[x][1]]=tag[ch[x][1]]^1;
20         swap(ch[x][0],ch[x][1]);
21         tag[x]=0;
22     }
23 }
24 int build_tree(int fa,int l,int r)
25 {
26     if(l>r) return 0;
27     int mid=(l+r)>>1,
28         now=++sz;
29     key[now]=data[mid];f[now]=fa;tag[now]=0;
30     ch[now][0]=build_tree(now,l,mid-1);
31     ch[now][1]=build_tree(now,mid+1,r);
32     pushup(now);
33     return now;
34 }
35 void rotate(int x)
36 {
37     int old=f[x],oldf=f[old],which=get(x);
38     pushdown(oldf),pushdown(old),pushdown(x);
39     ch[old][which]=ch[x][which^1];f[ch[old][which]]=old;
40     ch[x][which^1]=old;f[old]=x;
41     f[x]=oldf;
42     if(oldf) ch[oldf][ch[oldf][1]==old]=x;
43     pushup(old),pushup(x);
44 }
45 void splay(int x,int goal)
46 {
47     for(int fa;(fa=f[x])!=goal;rotate(x))
48         if(f[fa]!=goal)        
49             rotate(get(fa)==get(x)?fa:x);
50     if(!goal)rt=x;
51 }
52 int rnk(int x)
53 {
54     int now=rt;
55     while(1)
56     {
57         pushdown(now);
58         if(x<=size[ch[now][0]])now=ch[now][0];
59         else
60         {
61             x-=size[ch[now][0]]+1;
62             if(!x)return now;
63             now=ch[now][1];
64         }
65     }
66 }
67 void turn(int l,int r)
68 {
69     l=rnk(l);
70     r=rnk(r+2);
71     splay(l,0);
72     splay(r,l);
73     pushdown(rt);
74     tag[ch[ch[rt][1]][0]]^=1;
75 }
76 void write(int now)
77 {
78     pushdown(now);
79     if(ch[now][0])write(ch[now][0]);
80     if(key[now]!=INF && key[now]!=-INF)cout<<key[now]<<" ";
81     if(ch[now][1]) write(ch[now][1]);
82 }
83 signed main()
84 {
85     cin>>n>>m;
86     for(int i=1;i<=n;i++)data[i+1]=i;
87     data[1]=-INF,data[n+2]=INF;
88     rt=build_tree(0,1,n+2);
89     int x,y;
90     for(int i=1;i<=m;i++)
91     {
92         cin>>x>>y;
93         turn(x,y);
94     }
95     write(rt);
96     return 0;
97 }
View Code

 

posted @ 2019-06-13 17:21  Al_Ca  阅读(274)  评论(0编辑  收藏  举报
ヾ(≧O≦)〃嗷~