您的位置:首页 > 其它

POJ 3580 splay模板题

2016-08-16 15:25 330 查看
POJ
3580


splay超全模板题...

总共有6种操作,分别为MIN,INSERT,ADD,DELETE,REVERSE,REVOLVE六种,下面会分别介绍格式与方法

首先输入N以及N个数,接下来输入M,即操作个数,接下来M行包含操作,具体格式如下

1.MIN a b 求a,b区间的最小值

2.ADD a b c 对a,b区间的每个数添加c

3.DELETE a 将第a个数删除

4.INSERT a b 在第a个数后面添加一个数b

5.REVERSE a,b 将a,b区间每个数翻转 如 REVERSE 2 4 (1,2,3,4,5)-->(1,4,3,2,5)

6.REVOLVE a,b,c 将a,b区间作为一个环 然后旋转c次(这是我的理解... ...)

如 REVOVLE 2 4 1 (1,2,3,4,5)-->(1,4,2,3,5)

REVOVLE
2 4 2 (1,2,3,4,5)-->(1,3,4,2,5)

然后这里面的c有可能是负数并且很大...
... 所以要取模

讲一下算法思想:

构建一棵splay树 储存父亲 左右儿子 翻转标记 增加标记 最小值 本身的值 以此节点为根的子树的节点数 (size)

首先将1~n节点添加进去 同时要在最前面和最后面添加一个节点(具体理由后面讲)

基本函数:update(更新这个节点的MIN值与size),splay(将一个点旋转到目标位置),rotateto(找到第k个节点并旋转到目标位置),pushdown(将增加标记(add)与翻转标记(rev)推到下一层节点),newnode(构建一个新节点,节点序号为cnt)

讲讲每个操作的具体操作方法

1.MIN a,b 将第a个节点旋到根节点,将第b+2个节点旋到根节点的右子树,注意:因为前面添加了一个节点,所以原来第a个节点为现在的第a+1个节点 所以现在根节点右节点的左子树就代表以前的 a,b区间,只要输出它的MIN就可以了

2.ADD a,b,c 同理

将第a个节点旋到根节点,将第b+2个节点旋到根节点的右子树,给根节点右节点的左子树增加标记与数值,最小值添加c

3.DELETE
a 将第a个节点旋到根节点,第a+2个节点旋到根节点的右节点,那么根节点右节点的左子树有且仅有a+1这个节点(即原来的第a个节点),那么断绝他们的父子关系就可以了

4.INSERT
a b
将第a个节点旋到根节点,第a+1个节点旋到根节点的右节点,那么根节点右节点的左子树绝对没有节点,同样添加一个值为b的新节点,然后建立关系

5.REVERSE
a,b 与ADD相似 只要改变一下将第a个节点旋到根节点,将第b+2个节点旋到根节点的右子树,给根节点右节点的左子树更改REV标记

6.REVOLVE
a,b,c这个操作有一点复杂... ...实际上可以看出 这就是将这个区间里面分成两个部分然后交换位置,那我们先通过旋转找到这一段区间,将它从父亲节点那里并记录位置,然后又找到另一端区间的位置,将之前存下来区间放在这个区间的前面,就可以实现,这一段需要一定的理解,可以手动模拟一下发现规律,具体见代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=400010;
using namespace std;
int rt,m,n,a[maxn],cnt;
int val[maxn],Min[maxn],add[maxn],rev[maxn];
int ls[maxn],rs[maxn],fa[maxn],sz[maxn];
void newnode(int v)
{
sz[++cnt]=1;
val[cnt]=Min[cnt]=v;
fa[cnt]=ls[cnt]=rs[cnt]=add[cnt]=rev[cnt]=0;
}
void update(int x)
{
sz[x]=sz[ls[x]]+sz[rs[x]]+1;
Min[x]=val[x];
if(ls[x]&&Min[ls[x]]<Min[x])Min[x]=Min[ls[x]];
if(rs[x]&&Min[rs[x]]<Min[x])Min[x]=Min[rs[x]];
}
void pushdown(int x)
{
if(add[x]!=0)
{
if(ls[x])
{
add[ls[x]]+=add[x];
Min[ls[x]]+=add[x];
val[ls[x]]+=add[x];
}
if(rs[x])
{
add[rs[x]]+=add[x];
Min[rs[x]]+=add[x];
val[rs[x]]+=add[x];
}
add[x]=0;
}
if(rev[x])
{
if(ls[x])rev[ls[x]]^=1;
if(rs[x])rev[rs[x]]^=1;
swap(ls[x],rs[x]);
rev[x]=0;
}
}
void build(int x,int f)
{
if(x==n+2)return;
newnode(a[x]);
if(f)
{
fa[cnt]=f;
rs[f]=cnt;
}
else rt=cnt;
int now=cnt;
build(x+1,cnt);
update(now);
}
void init()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(0,0);
}
void rrotate(int x)
{
int y=ls[x],k=fa[x];
ls[x]=rs[y];
if(ls[x])fa[ls[x]]=x;
fa[x]=y;fa[y]=k;rs[y]=x;
if(k==0)rt=y;
else
if(ls[k]==x)ls[k]=y;
else rs[k]=y;
update(x);
}
void lrotate(int x)
{
int y=rs[x],k=fa[x];
rs[x]=ls[y];
if(rs[x])fa[rs[x]]=x;
ls[y]=x;fa[x]=y;fa[y]=k;
if(k==0)rt=y;
else
if(ls[k]==x)ls[k]=y;
else rs[k]=y;
update(x);
}
void splay(int x,int f)
{
int y,z;
while(1)
{
if((y=fa[x])==f)break;
if((z=fa[y])==f)
{
if(ls[y]==x)rrotate(y);
else lrotate(y);
}
else
{
if(ls[z]==y)
{
if(ls[y]==x)rrotate(z),rrotate(y);
else lrotate(y),rrotate(z);
}
else
{
if(rs[y]==x)lrotate(z),lrotate(y);
else rrotate(y),lrotate(z);
}
}
}
return;
}
void rotateto(int k,int f)
{
int i=rt;
while(1)
{
pushdown(i);
int lsum=sz[ls[i]]+1;
if(k==lsum)break;
if(k<lsum)i=ls[i];
else k-=lsum,i=rs[i];
}
splay(i,f);
}
void ADD(int x,int y,int z)
{
rotateto(x,0);
rotateto(y+2,rt);
int k=ls[rs[rt]];
add[k]+=z,val[k]+=z,Min[k]+=z;
}
void insert(int x,int y)
{
rotateto(x+1,0);rotateto(x+2,rt);
newnode(y);
ls[rs[rt]]=cnt;
fa[cnt]=rs[rt];
}
void del(int x)
{
rotateto(x,0);rotateto(x+2,rt);
ls[rs[rt]]=0;
}
void MIN(int x,int y)
{
rotateto(x,0);rotateto(y+2,rt);
printf("%d\n",Min[ls[rs[rt]]]);
}
void reverse(int x,int y)
{
if(x==y)return;
rotateto(x,0);rotateto(y+2,rt);
rev[ls[rs[rt]]]^=1;
}
void revolve(int x,int y,int z)
{
int p=(y-x+1),k,t;
k=(z%p+p)%p;
if(k)
{
rotateto(y-k+1,0);rotateto(y+2,rt);
t=ls[rs[rt]];
ls[rs[rt]]=0;
rotateto(x,0);rotateto(x+1,rt);
ls[rs[rt]]=t;
fa[t]=rs[rt];
}
}
void solve()
{
int x,y,z;
char s[10];
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",s);
if(s[0]=='A')
{
scanf("%d%d%d",&x,&y,&z);
ADD(x,y,z);
}
else
if(s[0]=='I')
{
scanf("%d%d",&x,&y);
insert(x,y);
}
else
if(s[0]=='D')
{
scanf("%d",&x);
del(x);
}
else
if(s[0]=='M')
{
scanf("%d%d",&x,&y);
MIN(x,y);
}
else
if(s[0]=='R')
{
if(s[3]=='O')
{
scanf("%d%d%d",&x,&y,&z);
revolve(x,y,z);
}
else
{
scanf("%d%d",&x,&y);
reverse(x,y);
}
}
}
return;
}
int main()
{
init();
solve();
return 0;
}好像差不多了...
...代码AC了,但时间有点长... ...毕竟还是第一次做个题目,借鉴了一下别人的代码... ...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  POJ3580 splay