很神奇的旋转操作。

目前没看到其他数据结构能实现这个功能。平衡树不好处理区间操作,线段树很难旋转。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. JMS-activeMq发布订阅模式(非持久订阅)

    Publisher的代码: import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Deli ...

  2. list 组合,模糊查询llist 数据(不走数据库)

    @ResponseBody    @POST    @Path("/megerPerson/{realName}")    public ResultEntity partnerL ...

  3. ubuntu ss 搭建(tizi_服务端

    #更新源 apt-get update #安装python和pip apt-get install python-gevent python pip #安装ss pip install shadows ...

  4. poj 2409 Let it Bead Polya计数

    旋转能够分为n种置换,相应的循环个数各自是gcd(n,i),个i=0时不动,有n个 翻转分为奇偶讨论,奇数时有n种置换,每种有n/2+1个 偶数时有n种置换,一半是n/2+1个,一半是n/2个 啃论文 ...

  5. strults2标签s:set的用法

    struts2标签<s:set></s:set>的用法.原理 http://liuna718-163-com.iteye.com/blog/1124654     我的例子: ...

  6. MySQL学习总结(四)数据的基本操作以及MySQL运算符和常用函数

    数据库是存储数据库对象的仓库,数据库的基本对象是表,表用来存储数据.关于数据的操作也就是我们常说的CRUD,C指的是CREATE(插入数据记录).R指的是READ(查询数据记录).U指的是UPDATE ...

  7. chrome 一进入调试页面就会自己主动打断点

    近期在用chrome调试页面时.一刷新页面总会自己主动打断点.自己鼓捣了一下,发现 把它改为这个样子的时候就能够解决问题,即把调试面板上的第5个button从Deactivate breakpoint ...

  8. sql server 数据库基础知识(二)

    CASE函数用法1:单值判断,相当于switch caseCASE expression WHEN value1 THEN returnvalue1 WHEN value2 THEN returnva ...

  9. 在ubuntu下安装ns2-allinone-2.35.tar.gz

    1.软件下载 首先先下载ns-allinone-2.35.tar.gz (下载路径http://sourceforge.net/projects/nsnam/files/),将其放到你/home/my ...

  10. 基于GitLab的前端Assets发布体系

    以SVN+RMS为核心的发布系统,对前端开发的影响上来看,存在以下问题: 覆盖式的发布,容易导致线上问题. js一旦发布,就有可能被任意其他页面使用.被引用的越多,就越重要.一旦核心js出现故障,影响 ...