您的位置:首页 > 其它

hdu3487 (splay伸展树 区间翻转,切割,插入)

2015-05-31 09:49 330 查看
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string.h>
#include <queue>
#define N 300010
using namespace std;  //hdu3487 (splay伸展树 区间翻转,切割,插入)
int pre
, key
, ch
[2], size
, rev
, root, tot, n, cnt; //rev是翻转的延时标志
/*
伸展树的一点心得---   rotate、Splay操作是不影响伸展树原有的性质(只要是节点的左孩子,那它就小于节点,节点的右孩子那他就大于节点)
判断节点(k)在其父亲节点(x)的左孩子还是右孩子时用ch[x][ch[x][1]==k]=k
只要是建立起了一棵满足伸展树性质的,那都可以使用rotate、Splay操作,伸展树性质不变
另外对于要pushdown或update,都要记得时常更新,否则很容易TLE
*/
void update(int x)  //区间维护
{
size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
return ;
}
void pushdown(int x)  //传递下去
{
if(rev[x])
{
int k=ch[x][0];
ch[x][0]=ch[x][1];
ch[x][1]=k;
rev[ch[x][0]]^=1;
rev[ch[x][1]]^=1;
rev[x]=0;
}
return ;
}
void newnode(int &r, int father, int k)   //初始化
{
r=++tot;
pre[r]=father;
key[r]=k;
ch[r][0]=ch[r][1]=rev[r]=0;
size[r]=1;
return ;
}
void build(int l, int r, int father, int &k)  //分治法,建立伸展树
{
if(l>r)return ;
int mid=(l+r)>>1;
newnode(k, father, mid);
build(l, mid-1, k, ch[k][0]);
build(mid+1, r, k, ch[k][1]);
update(k);
return ;
}
void init()//数据初始化,开头建立两个节点,一个是伸展树的根(相当于最小),一个是伸展树根的右节点(相当于最大)----作为边界对程序操作无影响,因为你截取的区间都在这中间
{
root=tot=ch[0][0]=ch[0][1]=pre[0]=size[0]=rev[0]=0;
newnode(root, 0, -1);
newnode(ch[root][1], root, -1);
update(root);
build(1, n, ch[root][1], ch[ch[root][1]][0]);
return ;
}
void rotate(int x, int kind)
{
int k=pre[x];
pushdown(k);
pushdown(x);
ch[k][!kind]=ch[x][kind];
pre[ch[x][kind]]=k;
ch[x][kind]=k;
pre[x]=pre[k];
if(pre[k])
ch[pre[k]][ch[pre[k]][1]==k]=x;
pre[k]=x;
update(k);
return ;
}
void Splay(int x, int goal)
{
int k, g, h;
pushdown(x);
while(pre[x]!=goal)
{
k=pre[x];
if(pre[k]==goal)
{
rotate(x, ch[k][0]==x);
}
else
{
g=(ch[k][0]==x?0:1);
h=(ch[pre[k]][0]==k?0:1);
if(g==h)
{
rotate(k, !g);
rotate(x, !g);
}
else
{
rotate(x, !g);
rotate(x, g);
}
}
}
if(goal==0)root=x;
update(x);
return ;
}
int select(int k, int r)
{
pushdown(r);
if(size[ch[r][0]]+1==k)
return r;
else if(size[ch[r][0]]+1>k)
return select(k, ch[r][0]);
else return select(k-size[ch[r][0]]-1, ch[r][1]);
}
void reversal(int l, int r)
{
int j, g;
j=select(l, root);
g=select(r+2, root);
Splay(j, 0);
Splay(g, root);
rev[ch[g][0]]^=1;    //异或
return ;
}
int getmin(int r)
{
int s=r;
pushdown(r);        //注意pushdown下去,否则TLE
while(r)
{
s=r;
r=ch[r][0];
pushdown(r);
}
return s;
}

void cut(int l, int r, int k)  //切割区间并插入
{
int j, g, h;
j=select(l, root); //因为开头已经建立了一个最小的节点,每个输入的元素的排名要+1
g=select(r+2, root);
Splay(j, 0);
Splay(g, root);
h=ch[g][0]; //其左子树就是实际[l,r]区间里的数
ch[g][0]=0;
update(g);
update(j);
j=select(k+1, root);
Splay(j, 0);
g=getmin(ch[root][1]);
Splay(g, root);
ch[g][0]=h;//相当于插到k后面
pre[ch[g][0]]=g;
update(g);
update(j);
return ;
}

void print(int r) //输出,递归从伸展树的左边到伸展树的右边
{
if(cnt==n)return ;
if(r==0)return ;
pushdown(r);
print(ch[r][0]);
if(key[r]>0)
{
printf("%d", key[r]);
cnt++;
if(cnt<n)printf(" ");
}
print(ch[r][1]);
}

int main()
{
int m, a, b, c;
char p[10];
while(scanf("%d%d", &n, &m)!=EOF)
{
if(n<0&&m<0)break;
init();
while(m--)
{
scanf("%s", &p);
if(p[0]=='C')
{
scanf("%d%d%d", &a, &b, &c);
cut(a, b, c);
}
if(p[0]=='F')
{
scanf("%d%d", &a, &b);
reversal(a, b);
}
}
cnt=0;
print(root);
printf("\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: