很神奇的旋转操作。

目前没看到其他数据结构能实现这个功能。平衡树不好处理区间操作,线段树很难旋转。splay tree搞这个就很简单了。

下面用的这个模板跑了700ms,好慢,估计是删除操作太费时了,是时候去找找其他更快的模板了。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std; #define MAXN 100100 //好慢啊 优化下
bool Add[MAXN];//延迟标记 struct Splay_Tree
{
int cnt, rt;//cnt为节点数,rt == root struct Tree{
int key;//关键字
int num, size;//num是这个节点有多少重复,size是以这个节点为根的子树大小。
int fa, son[];
}T[MAXN]; inline void init()
{
cnt = ;//初始化超级根节点(标记为0的节点)
T[].size = ;
rt = ;
memset(Add,,sizeof(Add));//开始初始化0
}
inline void PushUp(int x)
{
T[x].size=T[T[x].son[]].size+T[T[x].son[]].size+T[x].num;
} inline void PushDown(int x)
{
//翻转操作,这一步最为关键
if(Add[x])
{
if(T[x].son[])//
{
int son0 = T[x].son[];
//不管那么多,先旋转起来。
swap(T[son0].son[],T[son0].son[]);
Add[son0] ^= ;
}
if(T[x].son[])
{
int son1 = T[x].son[];
//不管那么多,先旋转起来。
swap(T[son1].son[],T[son1].son[]);
Add[son1] ^= ;
}
Add[x]=;//不管子节点有没有,这层一定往下推,没有子节点相当于标记无效。
}
} inline int Newnode(int key, int fa) //新建一个节点并返回
{
++cnt;
T[cnt].key=key;
T[cnt].num=T[cnt].size=;
T[cnt].fa=fa;
T[cnt].son[]=T[cnt].son[]=;
return cnt;
} inline void Rotate(int x, int p) //0左旋 1右旋
{
int y=T[x].fa;
PushDown(y);//你是说这个有问题。
PushDown(x);
T[y].son[!p]=T[x].son[p];
T[T[x].son[p]].fa=y;
T[x].fa=T[y].fa;
if(T[x].fa)
T[T[x].fa].son[T[T[x].fa].son[] == y]=x;
T[x].son[p]=y;
T[y].fa=x;
PushUp(y);
PushUp(x);
} void Splay(int x, int To) //将x节点移动到To的子节点中
{
while(T[x].fa != To)
{ if(T[T[x].fa].fa == To)
{
//在这里面得
PushDown(T[x].fa);
PushDown(x);
Rotate(x, T[T[x].fa].son[] == x);
}
else
{
int y=T[x].fa, z=T[y].fa;
PushDown(z);
PushDown(y);
PushDown(x);
int p=(T[z].son[] == y);
if(T[y].son[p] == x)
Rotate(x, !p), Rotate(x, p); //之字旋
else
Rotate(y, p), Rotate(x, p); //一字旋
}
}
if(To == ) rt=x;
} int GetPth(int p, int To) //返回第p小的节点 并移动到To的子节点中
{
if(!rt || p > T[rt].size) return ;
int x=rt;
while(x)
{
PushDown(x);
if(p >= T[T[x].son[]].size+ && p <= T[T[x].son[]].size+T[x].num)
break;
if(p > T[T[x].son[]].size+T[x].num)
{
p-=T[T[x].son[]].size+T[x].num;
x=T[x].son[];
}
else
x=T[x].son[];
}
Splay(x, );
return x;
} int Find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处
{
if(!rt) return ;
int x=rt;
while(x)
{
PushDown(x);
if(T[x].key == key) break;
x=T[x].son[key > T[x].key];
}
if(x) Splay(x, );
return x;
} int Prev() //返回根节点的前驱 非重点
{
if(!rt || !T[rt].son[]) return ;
int x=T[rt].son[];
while(T[x].son[])
{
PushDown(x);
x=T[x].son[];
}
Splay(x, );
return x;
} int next() //返回根结点的后继 非重点
{
if(!rt || !T[rt].son[]) return ;
int x=T[rt].son[];
while(T[x].son[])
{
PushDown(x);
x=T[x].son[];
}
Splay(x, );
return x;
} void Insert(int key) //插入key值
{
if(!rt)
rt=Newnode(key, );
else
{
int x=rt, y=;
while(x)
{
PushDown(x);
y=x;
if(T[x].key == key)
{
T[x].num++;
T[x].size++;
break;
}
T[x].size++;//既然一定调整
x=T[x].son[key > T[x].key];
}
if(!x)
x = T[y].son[key > T[y].key] = Newnode(key, y);
Splay(x, );
}
} void Delete(int key)
{
//我知道什么错误了,删除一个节点前,需要先将这个点splay 到根
int x = key;
if(!x) return;
Splay(x, );
if(T[x].num>)
{
T[x].num--;
PushUp(x);
return;
}
int y=T[x].son[];
PushDown(y);
//终于找到了,只要往下找就得pushdown
while(T[y].son[])
{
y=T[y].son[];
PushDown(y);
}
int z=T[x].son[];
PushDown(z);
while(T[z].son[])
{
z=T[z].son[];
PushDown(z);
}
if(!y && !z)
{
rt=;
return;
}
if(!y)
{
Splay(z, );
T[z].son[]=;
PushUp(z);
return;
}
if(!z)
{
Splay(y, );
T[y].son[]=;
PushUp(y);
return;
}
Splay(y, );
Splay(z, y);//前驱和后继相同是什么鬼
T[z].son[]=;
PushUp(z);
PushUp(y);
} // int GetRank(int key) //获得值<=key的节点个数 并将其转移到根处 若<key只需将<=换为<
// {
// //我没有写PUSH_UP 和 PUSH_DOWN
// if(!rt) return 0;
// int x=rt, ret=0, y=0;
// while(x)
// {
// y=x;
// if(T[x].key <= key)
// {
// ret += T[T[x].son[0]].size + T[x].num;
// x=T[x].son[1];
// }
// else
// x=T[x].son[0];
// }
// Splay(y, 0);
// return ret;
// } // 这个删除太丑了
// void Delete(int l, int r) //删除值在[l, r]中的所有节点 l!=r
// {
// if(!Find(l)) Insert(l);// 你这样写真的好吗? 泥煤
// int p=Prev();
// if(!Find(r)) Insert(r);
// int q=next();
// if(!p && !q)
// {
// rt=0;
// return;
// }
// if(!p)
// {
// T[rt].son[0]=0;
// PushUp(rt);
// return;
// }
// if(!q)
// {
// Splay(p, 0);
// T[rt].son[1]=0;
// PushUp(rt);
// return;
// }
// Splay(p, q);
// T[p].son[1]=0;
// PushUp(p);
// PushUp(q);
// } void display(int x)
{
if(x==) return ;
PushDown(x);
display(T[x].son[]);
//printf("%d ",T[x].key);
display(T[x].son[]);
} }spt; struct node
{
int key;
int id;
}g[MAXN]; int cmp(node t1,node t2)
{
if(t1.key == t2.key) return t1.id<t2.id;
return t1.key<t2.key;
} int main() {
//SPT 独特的旋转操作!
int n;
//printf("nishi sb?");
while(scanf("%d",&n) && n)
{
spt.init();
for(int i=;i<=n;i++)
{
int tmp;
scanf("%d",&tmp);
g[i].key = tmp;
g[i].id = i;
spt.Insert(i);
//只是默认了,每个数字的id 就是它在splay 中对应的下标
}
//死循环什么鬼。
//printf("nishi sb ma?");
sort(g+,g++n,cmp);
for(int i=;i<=n;i++)//开始旋转
{
//step one 将当前最小的点,移动到树根处
spt.Splay(g[i].id, );
//spt.display(spt.rt);
//printf("\n");
//step two 将整个左子树旋转
int sonid = spt.T[g[i].id].son[];
printf("%d",spt.T[sonid].size+i);
if(i!=n) printf(" ");
if(sonid != )
{
swap(spt.T[sonid].son[],spt.T[sonid].son[]); Add[sonid] ^= ;
}
//这里那里GG了,果真还是这里有问题。
//第一次就删除了两个,不能看
//spt.display(spt.rt);
//printf("\n");
spt.Delete(g[i].id);
//每次操作之后,都把结果打印一遍
//spt.display(spt.rt);
//printf("\n");
}
printf("\n");
}
return ;
}

splay tree旋转操作 hdu 1890的更多相关文章

  1. hdu 1890 Robotic SortI(splay区间旋转操作)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1890 题解:splay又一高级的功能,区间旋转这个是用线段树这些实现不了的,这题可以学习splay的旋 ...

  2. [AHOI2006]文本编辑器 Splay tree区间操作

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1269 Description 这些日子,可可不和卡卡一起玩了,原来可可正废寝忘食的想做一个 ...

  3. HDU 1890 Robotic Sort (splay tree)

    Robotic Sort Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tota ...

  4. hdu 1890 Robotic Sort(splay 区间反转+删点)

    题目链接:hdu 1890 Robotic Sort 题意: 给你n个数,每次找到第i小的数的位置,然后输出这个位置,然后将这个位置前面的数翻转一下,然后删除这个数,这样执行n次. 题解: 典型的sp ...

  5. Splay Tree的删除操作

    Splay Tree的插入操作,搜索操作,和删除操作都实现了,那么就能够使用来解题了. 指针的删除操作的处理还是那么难的,非常多坎须要避开. 同一个坎还是坑了我好多次,就是指针传递的问题,什么时候须要 ...

  6. hdu 2871 Memory Control(伸展树splay tree)

    hdu 2871 Memory Control 题意:就是对一个区间的四种操作,NEW x,占据最左边的连续的x个单元,Free x 把x单元所占的连续区间清空 , Get x 把第x次占据的区间输出 ...

  7. HDU 4453 Looploop (伸展树splay tree)

    Looploop Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  8. HDU1890 Robotic Sort Splay tree反转,删除

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1890 题目中涉及数的反转和删除操作,需要用Splay tree来实现.首先对数列排序,得到每个数在数列 ...

  9. HDU-3436 Queue-jumpers 树状数组 | Splay tree删除,移动

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3436 树状数组做法<猛戳> Splay tree的经典题目,有删除和移动操作.首先要离散化 ...

随机推荐

  1. [TypeScript] Dynamically initialize class properties using TypeScript decorators

    Decorators are a powerful feature of TypeScript that allow for efficient and readable abstractions w ...

  2. JQuery日记_5.14 Sizzle选择器(七)

    上篇说道,tokenize方法会把selector切割成一个个selector逻辑单元(如div>a是三个逻辑单元 'div','>','a')并为之片段赋予相应类型的过滤函数. for ...

  3. angular - 如何运行在起来 - 使用nginx

    nginx下载地址,使用的是标准版的: 点击下载nginx nginx下载完后,解压 dist文件夹下面所有angular文件放入html文件夹中. 最后命令行cd到当前nginx.exe目录,启动命 ...

  4. 一张图片教会你写mysql 语句

    MySQL的语句执行顺序 MySQL的语句一共分为11步,如下图所标注的那样,最先执行的总是FROM操作,最后执行的是LIMIT操作.其中每一个操作都会产生一张虚拟的表,这个虚拟的表作为一个处理的输入 ...

  5. c与c++中的强制类型转换区别

    强制类型转换的一般形式为: (类型名)(表达式) 如:(int)a.这是C语言使用的形式,C++把它保留了下来,以利于兼容. C++还增加了以下形式: 类型名(表达式) 如:int(a).这种形式类似 ...

  6. LDAP简介及LDAP服务器的安装与配置

    一.LDAP简介 全称:Lightweight Directory Access Protocol,目录服务是一种特殊的数据库系统,其专门针对读取. LDAP目录中的信息是按照树型结构组织,具体信息存 ...

  7. Cocos2dx报OpenGL error 0x0506错误

    近期做第三方sdk接入时,发现iOS8系统下,进行银联充值后,返回游戏有很大几率会报 OpenGL error 0x0506............ 之类的绘制问题,游戏卡死,花了很长时间,一直没有头 ...

  8. (016)给定一个有序数组(递增),敲代码构建一棵具有最小高度的二叉树(keep it up)

    给定一个有序数组(递增),敲代码构建一棵具有最小高度的二叉树. 因为数组是递增有序的.每次都在中间创建结点,类似二分查找的方法来间最小树. struct TreeNode { int data; Tr ...

  9. 光栅化规则(Rasterization Rules)

    光栅化规则不是唯一的,只要能满足在扫描线填充过程中,对于一条分割线两边的像素能够被不重复不遗漏地填充即可. 在gdi3d中目前使用的是下面光栅化规则: xLeft_int=ceil(xLeft-0.5 ...

  10. RPC框架-yar学习

    RPC采用客户机/服务器模式. 请求程序就是一个客户机,而服务提供程序就是一个服务器.首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息.在服务器端, 进程保持睡眠状态直到调 ...