HDU 4453:Looploop(Splay各种操作)
http://acm.hdu.edu.cn/showproblem.php?pid=4453
题意:很多种操作:1、add x,将从光标起的 k2 个数全部加上 x;2、reverse,将从光标起的 k1 个数全部反转;3、insert x,在光标处的后一位插入值为 x 的数;4、delete,删除光标所在位置的数;5、move x,如果x是2,将光标右移,否则将光标左移。6、查询光标所在位置的值。
思路:在ACM中第一次写了6000+bytes的代码,把Splay几乎所有操作都汇集了,是一个很好的入门题目(虽然写了好久好久)。Splay是先插入两个结点,即0号节点下面的root,root的右孩子。Splay是一棵二叉树,满足左儿子比节点小,右儿子比节点大,中序遍历出来的结果就是原来数组的结果。所以我们在插入数组的时候,只要将数组插入到 ch[root][1] 的左孩子 ch[ch[root][1][0] (我的代码中的keytree) 的位置,那么就可以对这些数进行区间操作,并且这些数都是按顺序的。Splay就是利用这样的性质来完成各种操作的。注意因为一开始插入了两个点,所以实际上SplayTree的节点有 n + 2 个,数组中的元素标号从 2 开始到 n + 1。还要注意有 Insert 操作所以数组开大点,有一次TLE是因为这个。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <string>
#include <iostream>
#include <stack>
#include <map>
#include <queue>
using namespace std;
#define N 1000010
#define INF 0x3f3f3f3f
#define lson ch[x][0]
#define rson ch[x][1]
#define keytree ch[ch[root][1]][0]
struct SplayTree
{
int fa[N], ch[N][], col[N], rev[N], sz[N], val[N], cnt, root, num[N];
int n, tol, k1, k2; int NewNode(int w, int f, int kind)
{
cnt++;
rev[cnt] = col[cnt] = ch[cnt][] = ch[cnt][] = ;
sz[cnt] = ; val[cnt] = w; fa[cnt] = f;
ch[f][kind] = cnt;
return cnt;
} void PushUp(int x)
{
sz[x] = sz[lson] + sz[rson] + ;
} void PushDown(int x)
{
if(rev[x]) {
swap(lson, rson);
if(lson) rev[lson] ^= ;
if(rson) rev[rson] ^= ;
rev[x] = ;
}
if(col[x]) {
if(lson) {
col[lson] += col[x];
val[lson] += col[x];
}
if(rson) {
col[rson] += col[x];
val[rson] += col[x];
}
col[x] = ;
}
} void Build(int l, int r, int &x, int f, int kind)
{
if(r < l) return ;
int m = (l + r) >> ;
x = NewNode(num[m], f, kind);
Build(l, m - , ch[x][], x, );
Build(m + , r, ch[x][], x, );
PushUp(x);
} void Init() // 初始化
{
cnt = root = ;
col[] = fa[] = rev[] = val[] = ch[][] = ch[][] = sz[] = ;
root = NewNode(, , ); // 先开两个节点,然后把数组元素放进 keytree 的位置
ch[root][] = NewNode(, root, );
sz[root] = ;
Build(, n, keytree, ch[root][], );
PushUp(ch[root][]); PushUp(root);
} void Rotate(int x, int kind)
{
int y = fa[x], z = fa[y];
PushDown(y); PushDown(x);
ch[y][!kind] = ch[x][kind];
if(ch[x][kind]) fa[ch[x][kind]] = y;
fa[y] = x; fa[x] = z;
if(z) {
if(ch[z][] == y) ch[z][] = x;
else ch[z][] = x;
}
ch[x][kind] = y;
PushUp(y);
} void Splay(int x, int goal)
{
while(fa[x] != goal) {
int y = fa[x], z = fa[y];
PushDown(z); PushDown(y); PushDown(x);
int kind1 = ch[y][] == x;
int kind2 = ch[z][] == y;
if(z == goal) {
Rotate(x, kind1);
} else {
if(kind1 == kind2) {
Rotate(y, kind2);
} else {
Rotate(x, kind1);
}
Rotate(x, kind2);
}
// printf("%d, %d, %d\n", x, fa[x], goal);
}
PushUp(x);
if(goal == ) root = x;
} void RTO(int k, int goal) // 将第k个元素旋转到0号节点下面
{
int x = root;
PushDown(x);
while() {
if(k <= sz[lson]) x = lson;
else if(k == sz[lson] + ) break;
else {
k -= sz[lson] + ;
x = rson;
}
PushDown(x);
}
Splay(x, goal);
} void Insert(int val, int index)
{
RTO(index, ); RTO(index + , root);
keytree = NewNode(val, ch[root][], );
PushUp(ch[root][]); PushUp(root);
// Splay(keytree, 0);
} int Delete(bool kind)
{
int w;
if(kind) {
RTO(, ); RTO(, root);
w = val[keytree];
keytree = ;
} else {
int ed = sz[root];
RTO(ed - , ); RTO(ed, root);
w = val[keytree];
keytree = ;
}
PushUp(ch[root][]); PushUp(root);
// Splay(root, 0);
return w;
} void Move(int kind) // 光标移动, 如果向左移动就删除最后的元素插到最前面, 向右移动反之
{
int w;
if(kind == ) {
w = Delete();
Insert(w, );
} else {
w = Delete();
Insert(w, sz[root] - );
}
} void Reverse()
{
RTO(, );
// Debug(root);
RTO(k1 + , root);
// puts("----------------");
// Debug(root);
// puts("----------------");
rev[keytree] ^= ;
// swap(ch[keytree][0], ch[keytree][1]);
PushUp(ch[root][]); PushUp(root);
} void Add(int w)
{
RTO(, ); RTO(k2 + , root);
// printf("add\n");
col[keytree] += w;
val[keytree] += w;
PushUp(ch[root][]); PushUp(root);
} int Query() // 查询操作将要查询的数移动到根节点直接查询
{
RTO(, );
// printf("%d\n", root);
return val[root];
} void Debug(int x)
{
if(lson) Debug(lson);
printf("%d : %d, %d, %d, %d\n", val[x], val[lson], val[rson], lson, rson);
if(rson) Debug(rson);
}
}splay; int main()
{
int q, cas = ;
while(~scanf("%d%d%d%d", &splay.n, &q, &splay.k1, &splay.k2), splay.n + q + splay.k1 + splay.k2) {
for(int i = ; i <= splay.n; i++) scanf("%d", &splay.num[i]);
splay.Init();
char s[];
printf("Case #%d:\n", cas++);
for(int i = ; i <= q; i++) {
scanf("%s", s);
int x;
if(s[] == 'q') printf("%d\n", splay.Query());
else if(s[] == 'a') {
scanf("%d", &x);
splay.Add(x);
} else if(s[] == 'r') {
splay.Reverse();
} else if(s[] == 'i') {
scanf("%d", &x);
splay.Insert(x, );
} else if(s[] == 'd') {
splay.Delete();
} else if(s[] == 'm') {
scanf("%d", &x);
splay.Move(x);
}
// splay.Debug(splay.root);
}
}
return ;
}
HDU 4453:Looploop(Splay各种操作)的更多相关文章
- HDU 4453 Looploop (伸展树splay tree)
Looploop Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- hdu 4453 splay
Looploop Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total S ...
- hdu 2871 线段树(各种操作)
Memory Control Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) T ...
- BZOJ1500: [NOI2005]维修数列 [splay序列操作]【学习笔记】
以前写过这道题了,但我把以前的内容删掉了,因为现在感觉没法看 重写! 题意: 维护一个数列,支持插入一段数,删除一段数,修改一段数,翻转一段数,查询区间和,区间最大子序列 splay序列操作裸题 需要 ...
- P2596 [ZJOI2006]书架 && Splay 区间操作(三)
P2596 [ZJOI2006]书架 题目描述 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书, ...
- HDU 1754 I Hate It (Splay 区间操作)
题目大意 维护一个序列,支持两种操作 操作一:将第x个元素的值修改为y 操作二:询问区间[x,y]内的元素的最大值 解题分析 splay的区间操作,事先加入两个编号最小和最大的点防止操作越界. 具体的 ...
- hdu 2475 BOX (splay)
版权声明:本文为博主原创文章,未经博主允许不得转载. hdu 2475 Splay树是一种神奇的东西... 题意: 有一些箱子,要么放在地上,要么放在某个箱子里面 . 现在有两种操作: (1) MOV ...
- 「BZOJ1251」序列终结者 (splay 区间操作)
题面: 1251: 序列终结者 Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 5367 Solved: 2323[Submit][Status][D ...
- P2042 [NOI2005]维护数列 && Splay区间操作(四)
到这里 \(A\) 了这题, \(Splay\) 就能算入好门了吧. 今天是个特殊的日子, \(NOI\) 出成绩, 大佬 \(Cu\) 不敢相信这一切这么快, 一下子机房就只剩我和 \(zrs\) ...
随机推荐
- 面向对象编程(九)——面向对象三大特性之继承以及重写、Object类的介绍
面向对象三大特性 面向对象三大特征:继承 :封装/隐藏 :多态(为了适应需求的多种变化,使代码变得更加通用!) 封装:主要实现了隐藏细节,对用户提供访问接口,无需关心方法的具体实现. 继承:很好的实现 ...
- strtok&strsep
strtok,strtok_r,strsep--extract tokens from strings Tje strsep() function was introduced as a replac ...
- 教你安装CentOS 6.5如何选择安装包
近来发现越来越多的运维小伙伴们都有最小化安装CentOS 6.5系统的洁癖,因此,找老男孩来咨询,这个“洁癖”好习惯啊,必须支持,,因此发布本文和大家分享下. (1)系统安装类型选择及自定义额外包组 ...
- play 之定时器job
play定时任务之job 要创建一个Job,只需要简单地继承play.jobs.Job类就可以. 一些常用的注解: @Every("1h") 每隔1小时执行一次 @ ...
- UICollectionView未充满时也可以滚动
当数据不多,collectionView.contentSize小于collectionView.frame.size的时候,UICollectionView是不会滚动的 self.Cov.alway ...
- Java基础之写文件——使用多个视图缓冲区(PrimesToFile2)
控制台程序.本例将对应于每个素数的数据以三个连续数据项的形式写入: 1.以二进制值表示的字符串长度值(最好是整型,但本例使用double类型): 2.素数值的字符串表示”Prime=nnn“,其中数字 ...
- java-语法
JAVA语法 1.标识符 1.定义:对各种变量.方法.类等进行命名的字符序列 2.规则:他的组成由字母.数字.$,数字不能出现在开始,不能和关键字重复,区分大小写 2.数据类型 1.分类 1基本数据类 ...
- 使用visual studio 2012 编译opencv2.4.9
最近,由于需要从opencv源码部分对opencv中的某个函数进行修改,以提升算法的速度,因此一直在尝试使用vs2012来编译opencv.期间不乏多次的失败.今天通过实验发现了自己编译的opencv ...
- SQL 过滤 having
select * from emp --having 对分组之后使用 --输出部门号 和 部门的平均工资 并且 平均工资 > 2000 select deptno, avg(sal) as &q ...
- 转:Curl详解
用途说明 curl命令是一个功能强大的网络工具,它能够通过http.ftp等方式下载文件,也能够上传文件.其实curl远不止前面所说的那些功能,大家可以通过man curl阅读手册页获取更多的信息.类 ...