您的位置:首页 > 其它

区间翻转问题 Splay

2017-11-25 11:19 141 查看

NKOJ 2504 区间翻转问题

问题描述

给你一个长度为N的序列{ai}和M个操作

1.查询第k个数的值

2.将第k个数增加d

3.查询一段区间的和

4.查询一段区间的最大值

5.将一段区间镜面翻转(例如序列{1,2,3,4,5,6},将从2到5的区间翻转后得到序列{1,5,4,3,2,6})

对于除操作2,5以外的操作,输出相应的答案

输入格式

第一行两个正整数N,M

第二行N个整数,为初始的序列

第三行到底M+2行,每行若干个整数

·如果第一个数为1,那么后面一个正整数k,表示查询第k个数的值

·如果第一个数是2,那么后面两个正整数k,d,表示将ak增加d

·如果第一个数为3,那么后面两个正整数l,r,表示查询从al到ar的区间和

·如果第一个数为4,那么后面两个正整数l,r,表示查询从al到ar的最大值

·如果第一个数为5,那么后面两个正整数l,r,表示翻转从al到ar的这个区间

输出格式

除操作2,5外每个操作输出占一行,一个整数,为本次提问的答案

样例输入

6 8

1 2 3 4 5 6

1 4

3 2 5

4 2 2

5 2 5

3 1 3

5 2 5

2 5 1

4 1 6

样例输出

4

14

2

10

6

数据规模

2<=N<=100000

1<=M<=100000

原序列1<=ai<=1000

每次1<=k<=N,1<=l<=r<=N,1<=d<=1000

来源

感谢nodgd命题并提供数据

Splay裸题,主要是留个纪念。

首先按照编号建立平衡树,具体方法是加两个虚拟节点1,N+2,再将原数列中的数按key值为编号+1插入平衡树。

对于区间操作的整体思想是:提取一个区间[l,r],就把key为(l+1)-1的节点旋转到根,再把key为(r+1)+1的节点旋转到根的右儿子。此时这个节点的左子树就正好是需要提取的区间。这个由二叉检索树的性质是正确的。

于是,要得到区间和、区间极值,只需要维护以某个节点为根的子树的和、子树中的权值最大值即可。这些容易在旋转的同时更新。

修改某个点的权值,我采用的是先将该点旋转到根,再更新根节点的Max,Sum,val值。也可以在查找该点的同时更新路径上所有点的Max,Sum值。

最后是区间翻转问题。提取区间后,只需要打上lazy标记,访问到的时候下放,交换左右两棵子树即可。虽然Splay是自下往上的过程,但是Splay前一定要先找到需要Splay到根的点。这个过程是自上往下的,此时下放即可,不会造成操作的冲突。

注意lazy与线段树里略有不同:线段树中打上lazy标记的点一定是已经处理完该点的操作了,而本题中是下放时才进行操作。所以打lazy标记时应当用lazy^=1而不是lazy=1。

代码:

#include<stdio.h>
#include<algorithm>
#define MAXN 100005
using namespace std;

int Tmax(int x,int y,int z)
{
if(x>=y&&x>=z)return x;
if(y>=z)return y;
return z;
}

int N;

int rt,tot,fa[MAXN],ls[MAXN],rs[MAXN],pri[MAXN],val[MAXN],lazy[MAXN],Sum[MAXN],Max[MAXN],Size[MAXN];

void Update(int x,int y)
{
Size[y]=Size[ls[y]]+Size[rs[y]]+1;
Size[x]=Size[ls[x]]+Size[rs[x]]+1;
Sum[y]=Sum[ls[y]]+Sum[rs[y]]+val[y];
Sum[x]=Sum[ls[x]]+Sum[rs[x]]+val[x];
Max[y]=Tmax(Max[ls[y]],Max[rs[y]],val[y]);
Max[x]=Tmax(Max[ls[x]],Max[rs[x]],val[x]);
}

void Putdown(int p)
{
lazy[ls[p]]^=1;lazy[rs[p]]^=1;lazy[p]=0;
swap(ls[p],rs[p]);
}

void Zig(int x)
{
int y=fa[x],z=fa[y];
if(z)
{
if(ls[z]==y)ls[z]=x;
else rs[z]=x;
}
fa[x]=z;fa[y]=x;fa[rs[x]]=y;
ls[y]=rs[x];rs[x]=y;
Update(x,y);
}

void Zag(int x)
{
int y=fa[x],z=fa[y];
if(z)
{
if(ls[z]==y)ls[z]=x;
else rs[z]=x;
}
fa[x]=z;fa[y]=x;fa[ls[x]]=y;
rs[y]=ls[x];ls[x]=y;
Update(x,y);
}

void Splay(int x,int t)
{
int y,z;
while(fa[x]!=t)
{
y=fa[x];z=fa[y];
if(z==t)
{
if(ls[y]==x)Zig(x);
else Zag(x);
}
else
{
if(ls[z]==y)
{
if(ls[y]==x)Zig(y),Zig(x);
else Zag(x),Zig(x);
}
else
{
if(rs[y]==x)Zag(y),Zag(x);
else Zig(x),Zag(x);
}
}
}
if(!t)rt=x;
}

void Ins(int k,int v)
{
tot++;
int p=rt;
while(p)
{
Size[p]++;
if(Max[p]<v)Max[p]=v;Sum[p]+=v;
if(k<pri[p])
{
if(!ls[p]){ls[p]=tot;break;}
p=ls[p];
}
else
{
if(!rs[p]){rs[p]=tot;break;}
p=rs[p];
}
}
Size[tot]=1;fa[tot]=p;pri[tot]=k;val[tot]=Max[tot]=Sum[tot]=v;
Splay(tot,0);
}

int GetKth(int k)
{
int p=rt;
while(p)
{
if(lazy[p])Putdown(p);
if(Size[ls[p]]+1==k)return p;
if(Size[ls[p]]>=k)p=ls[p];
else k-=Size[ls[p]]+1,p=rs[p];
}
}

void Add(int k,int d)
{
int p=GetKth(k+1);
Splay(p,0);
val[p]+=d;
Max[p]=Tmax(val[p],Max[ls[p]],Max[rs[p]]);
Sum[p]+=d;
}

void Prepare(int x,int y)
{
int p,q;
p=GetKth(x);Splay(p,0);
q=GetKth(y+2);Splay(q,rt);
}

int GetSum(int x,int y)
{
Prepare(x,y);
return Sum[ls[rs[rt]]];
}

int GetMax(int x,int y)
{
Prepare(x,y);
return Max[ls[rs[rt]]];
}

void Rev(int x,int y)
{
Prepare(x,y);
int p=ls[rs[rt]];
lazy[p]^=1;
}

int main()
{
int M,i,op,x,y,p;

scanf("%d%d",&N,&M);

Ins(1,0);Ins(N+2,0);
for(i=1;i<=N;i++)
{
scanf("%d",&x);
Ins(i+1,x);
}

while(M--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d",&x);
p=GetKth(x+1);Splay(p,0);
printf("%d\n",val[p]);
}
else if(op==2)
{
scanf("%d%d",&x,&y);
Add(x,y);
}
else if(op==3)
{
scanf("%d%d",&x,&y);
printf("%d\n",GetSum(x,y));
}
else if(op==4)
{
scanf("%d%d",&x,&y);
printf("%d\n",GetMax(x,y));
}
else
{
scanf("%d%d",&x,&y);
Rev(x,y);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: