[POJ3468] A Simple Problem with Integers

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[POJ3468] A Simple Problem with Integers相关的知识,希望对你有一定的参考价值。

虽然这是线段树的区间修改裸题,但还是用来练习一下splay吧

 

最近补了一下splay,发现它真的是太优越了

splay是一种平衡树,它通过一种操作‘splay‘来维持整棵树的平衡

splay($x$):把$x$通过旋转转到树根

很明显我们需要多次旋转才能把$x$旋转到根,我们先看一下单次旋转长什么样

技术分享

效果:被旋转的节点往上走

虽然仅仅依靠单旋就能实现splay,但这样做太慢了,所以我们引入了双旋,即通过两次旋转,调整节点$x$的同时顺便调整树的结构

关于为什么双旋快请看这里

设$fa(x)=y$,$fa(y)=z$

若$x$,$y$,$z$在同侧

技术分享

反过来同理

若$x$,$y$,$z$在异侧

技术分享

反过来同理

有了这几种旋转,我们可以写出splay($x$)的代码:

如果$fa(x)=root$直接单旋

否则分四种情况做双旋

重复,直到$root=x$

这样我们就实现了splay

各种插入,删除,查找都基于splay,每次操做完之后splay一下当前节点(让被访问频率高的节点更靠近树根)

 

很久以前就听闻splay是可以用于做区间操作的

最近自己找博客,问学长和同学,总算弄懂了

如果splay用于实现区间操作,它的排序关键字不是值而是下标

如果我们维护一个序列,我们应该把原序列用二分建成近似完全二叉树

技术分享

在每个节点上维护一些值,比如在这道题里面,我们需要维护子树大小,每个节点的权值,以它为根的子树的权值和

为了实现区间修改,我们还要维护一个类似线段树的懒惰标记

因为要做区间操作,所以我们必须用某种方法提取出对应的区间

一般地,我们提取区间$[l,r]$应该这样做

splay(l-1);
root=rson[l-1];
splay(r+1);
root=l-1;

技术分享

首先,稍微观察一下就能看出旋转不改变整棵树的中序遍历

而$l-1$的右子树中的所有数都大于$l-1$,$r+1$的左子树中的所有数都小于$r+1$,所以以$x$为根的子树就代表区间$[l,r]$

提取出区间之后,修改操作直接在$x$上添加懒惰标记,询问直接返回$sum[x]$

注意当$l=1$或$r=n$时只用旋转一次,当$l=1$且$r=n$时不用旋转

现在的问题是:如何维护懒惰标记?

我们在splay($x$)之前应该让$root$到$x$路径上的所有点的儿子(还有root)下传标记

然后再开始正常的splay

写zig($x$)时先pushup($y$)再pushup($x$)

zag同理

每次询问或修改之后,我们应该把提出的区间splay到根以更新懒惰标记

询问时应该先pushdown($x$)再输出sum[$x$]

总之,访问任何节点都应该把根到它的路径上的所有节点依次pushdown,修改还要在splay时顺便pushup,查询应该先pushdown

  1 #include<stdio.h>
  2 #define ll long long
  3 int ch[100010][2],siz[100010],fa[100010],root,tot,n;
  4 ll sum[100010],add[100010],v[100010];
  5 int build(int l,int r){
  6     if(l==r){
  7         sum[l]=v[l];
  8         siz[l]=1;
  9         return l;
 10     }
 11     if(l+1==r){
 12         ch[r][0]=l;
 13         siz[r]=2;
 14         fa[l]=r;
 15         siz[l]=1;
 16         sum[l]=v[l];
 17         sum[r]=v[l]+v[r];
 18         return r;
 19     }
 20     int mid=(l+r)>>1,ls=0,rs=0;
 21     if(l<mid)ls=build(l,mid-1);
 22     if(mid<r)rs=build(mid+1,r);
 23     sum[mid]=v[mid];
 24     siz[mid]=1;
 25     if(ls){
 26         ch[mid][0]=ls;
 27         fa[ls]=mid;
 28         sum[mid]+=sum[ls];
 29         siz[mid]+=siz[ls];
 30     }
 31     if(rs){
 32         ch[mid][1]=rs;
 33         fa[rs]=mid;
 34         sum[mid]+=sum[rs];
 35         siz[mid]+=siz[rs];
 36     }
 37     return mid;
 38 }
 39 void pushup(int x){
 40     sum[x]=v[x]+sum[ch[x][0]]+sum[ch[x][1]];
 41     siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
 42 }
 43 void zig(int x){
 44     ll z,y,B;
 45     y=fa[x];
 46     z=fa[y];
 47     B=ch[x][1];
 48     if(root==y)root=x;
 49     fa[y]=x;
 50     fa[x]=z;
 51     if(B)fa[B]=y;
 52     ch[x][1]=y;
 53     ch[y][0]=B;
 54     if(ch[z][0]==y)ch[z][0]=x;
 55     if(ch[z][1]==y)ch[z][1]=x;
 56     pushup(y);
 57     pushup(x);
 58 }
 59 void zag(int y){
 60     ll x,z,B;
 61     x=fa[y];
 62     z=fa[x];
 63     B=ch[y][0];
 64     if(root==x)root=y;
 65     fa[x]=y;
 66     fa[y]=z;
 67     if(B)fa[B]=x;
 68     ch[y][0]=x;
 69     ch[x][1]=B;
 70     if(ch[z][0]==x)ch[z][0]=y;
 71     if(ch[z][1]==x)ch[z][1]=y;
 72     pushup(x);
 73     pushup(y);
 74 }
 75 void pushdown(int x){
 76     if(x&&add[x]){
 77         sum[x]+=add[x]*siz[x];
 78         v[x]+=add[x];
 79         if(ch[x][0])add[ch[x][0]]+=add[x];
 80         if(ch[x][1])add[ch[x][1]]+=add[x];
 81         add[x]=0;
 82     }
 83 }
 84 void splay(int x){
 85     if(x==0){
 86         root=0;
 87         return;
 88     }
 89     int y,z;
 90     pushdown(root);
 91     for(z=root;z!=x;){
 92         pushdown(ch[z][0]);
 93         pushdown(ch[z][1]);
 94         if(x<z)
 95             z=ch[z][0];
 96         else
 97             z=ch[z][1];
 98     }
 99     pushdown(ch[x][0]);
100     pushdown(ch[x][1]);
101     while(x!=root){
102         y=fa[x];
103         z=fa[y];
104         if(y==root){
105             if(ch[y][0]==x)
106                 zig(x);
107             else
108                 zag(x);
109         }else{
110             if(y==ch[z][0]){
111                 if(x==ch[y][0]){
112                     zig(y);
113                     zig(x);
114                 }else{
115                     zag(x);
116                     zig(x);
117                 }
118             }else{
119                 if(x==ch[y][0]){
120                     zig(x);
121                     zag(x);
122                 }else{
123                     zag(y);
124                     zag(x);
125                 }
126             }
127         }
128     }
129 }
130 void change(int l,int r,ll v){
131     if(l==1){
132         if(r==n)
133             add[root]+=v;
134         else{
135             splay(r+1);
136             add[ch[r+1][0]]+=v;
137             splay(ch[r+1][0]);
138         }
139         return;
140     }
141     if(r==n){
142         splay(l-1);
143         add[ch[l-1][1]]+=v;
144         splay(ch[l-1][1]);
145         return;
146     }
147     splay(l-1);
148     root=ch[l-1][1];
149     splay(r+1);
150     root=l-1;
151     add[ch[r+1][0]]+=v;
152     splay(ch[r+1][0]);
153 }
154 ll query(int l,int r){
155     ll ans;
156     if(l==1){
157         if(r==n){
158             pushdown(root);
159             return sum[root];
160         }else{
161             splay(r+1);
162             pushdown(ch[r+1][0]);
163             ans=sum[ch[r+1][0]];
164             splay(ch[r+1][0]);
165             return ans;
166         }
167     }
168     if(r==n){
169         splay(l-1);
170         pushdown(ch[l-1][1]);
171         ans=sum[ch[l-1][1]];
172         splay(ch[l-1][1]);
173         return ans;
174     }
175     splay(l-1);
176     root=ch[l-1][1];
177     splay(r+1);
178     root=l-1;
179     pushdown(ch[r+1][0]);
180     ans=sum[ch[r+1][0]];
181     splay(ch[r+1][0]);
182     return ans;
183 }
184 int main(){
185     int m,i,a,b,c;
186     char op[5];
187     scanf("%d%d",&n,&m);
188     for(i=1;i<=n;i++)scanf("%lld",v+i);
189     root=build(1,n);
190     while(m--){
191         scanf("%s%d%d",op,&a,&b);
192         if(op[0]==C){
193             scanf("%d",&c);
194             change(a,b,c);
195         }else
196             printf("%lld\\n",query(a,b));
197     }
198 }

以上是关于[POJ3468] A Simple Problem with Integers的主要内容,如果未能解决你的问题,请参考以下文章

A Simple Problem with Integers POJ - 3468

POJ - 3468 A Simple Problem with Integers

[poj3468]A Simple Problem with Integers

POJ3468 A Simple Problem with Integers 分块

POJ3468 a simple problem with integers 分块

POJ 3468 A Simple Problem with Integers 树状数组