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各种操作)的更多相关文章

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

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

  2. hdu 4453 splay

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

  3. hdu 2871 线段树(各种操作)

    Memory Control Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  4. BZOJ1500: [NOI2005]维修数列 [splay序列操作]【学习笔记】

    以前写过这道题了,但我把以前的内容删掉了,因为现在感觉没法看 重写! 题意: 维护一个数列,支持插入一段数,删除一段数,修改一段数,翻转一段数,查询区间和,区间最大子序列 splay序列操作裸题 需要 ...

  5. P2596 [ZJOI2006]书架 && Splay 区间操作(三)

    P2596 [ZJOI2006]书架 题目描述 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书, ...

  6. HDU 1754 I Hate It (Splay 区间操作)

    题目大意 维护一个序列,支持两种操作 操作一:将第x个元素的值修改为y 操作二:询问区间[x,y]内的元素的最大值 解题分析 splay的区间操作,事先加入两个编号最小和最大的点防止操作越界. 具体的 ...

  7. hdu 2475 BOX (splay)

    版权声明:本文为博主原创文章,未经博主允许不得转载. hdu 2475 Splay树是一种神奇的东西... 题意: 有一些箱子,要么放在地上,要么放在某个箱子里面 . 现在有两种操作: (1) MOV ...

  8. 「BZOJ1251」序列终结者 (splay 区间操作)

    题面: 1251: 序列终结者 Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 5367  Solved: 2323[Submit][Status][D ...

  9. P2042 [NOI2005]维护数列 && Splay区间操作(四)

    到这里 \(A\) 了这题, \(Splay\) 就能算入好门了吧. 今天是个特殊的日子, \(NOI\) 出成绩, 大佬 \(Cu\) 不敢相信这一切这么快, 一下子机房就只剩我和 \(zrs\) ...

随机推荐

  1. msvc库没有安装包,编译选项选择 代码生成 MT【多线程】,C#调用

    参考提过的一个问题,封装VC++动态链接库,C#调用,并将C#程序打包为exe安装包. 感谢大神.

  2. 注册页面的简单搭建(H5)

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. 离线下载Windows 调试符号 Symbols

    公司开发机没有不能连接到互联网.调试程序时那些Windows模块(如ntdll.dll)不能加载符号,而程序总是崩在这些模块里.想看一眼到底崩在了什么地方. 需要把对应的符号下载下来. 使用工具sym ...

  4. BeanUtils设置字段值失败问题

    package org.apache.commons.beanutils; import static org.junit.Assert.*; import java.beans.BeanInfo; ...

  5. 关于Eclipse的unsupported major minor version 51.0 错误

    把别人的工程 拿来运行报上述错误 是因为工程版本不对 解决办法:新建工程 把现有的代码或资源文件  拷到新建工程里

  6. Java基础之创建窗口——使用卡片布局管理器(TryCardLayout)

    控制台程序. 卡片布局管理器会生成一叠组件——一个组件放在另一个组件的上面.添加到容器中的第一个组件在堆栈的顶部,因此是可见的,添加的最后一个组件在堆栈的底部.使用默认的构造函数CardLayout( ...

  7. linux:档案与目录管理

    几个常见的目录处理命令: cd(change directory):变更目录 pwd(print working directory):显示当前目录[目录为连结档,则只显示连结档的路径]([-P]不以 ...

  8. c++l类

    c++类和C#中定义类的方法异同之处: 1. 相同处: 1.1.都需要使用 class标识: 1.2.都包含有成员:函数,属性: 1.3.都有private public protect 标识的成员 ...

  9. 利用JAVA计算TFIDF和Cosine相似度-学习版本

    写在前面的话,既然是学习版本,那么就不是一个好用的工程实现版本,整套代码全部使用List进行匹配效率可想而知. [原文转自]:http://computergodzilla.blogspot.com/ ...

  10. 学习jsp(2)

    @Webservlet 具体见:http://www.cnblogs.com/luxh/archive/2012/06/06/2537458.html. 我折腾半天才发现,在web.xml里注册了,删 ...