[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 分块