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. Android --ListView分页

    参考博客:Android ListView分页加载(服务端+android端)Demo 监听OnScrollListener事件 class OnListScrollListener implemen ...

  2. Thinkphp关闭缓存方法总结(转)

    ThinkPHP在数据缓存方面包括文件方式.共享内存方式和数据库方式在内的多种方式进行缓存,通过插件方式还可以增加以后需要的缓存类,让应用开发可以选择更加适合自己的缓存方式,从而有效地提高应用执行效率 ...

  3. Java基础之一组有用的类——使用正则表达式搜索子字符串(TryRegex)

    控制台程序. 正则表达式只是一个字符串,描述了在其他字符串中搜索匹配的模式.但这不是被动地进行字符序列匹配,正则表达式其实是一个微型程序,用于一种特殊的计算机——状态机.状态机并不是真正的机器,而是软 ...

  4. nginx:配置详细说明

    一.fastcgi param 详情: fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;#脚本文件请求的路径 fast ...

  5. ASP.NET的一般处理程序对数据的基本操作

    TableList.ashx: <%@ WebHandler Language="C#" Class="TableList" %> using Sy ...

  6. Python学习总结6:字符串格式化操作及方法总结

    1. 格式化操作(%) Python中内置有对字符串进行格式化的操作. 模板 格式化字符串时,Python使用一个字符串作为模板.模板中有格式符,这些格式符为真实值预留位置,并说明真实数值应该呈现的格 ...

  7. ArrayList和LinkList区别

    ArrayList和LinkList区别 前者是数组的数据结构,后者是链表的数据结构 前者应用于排序和查找,后者应用于插入删除

  8. demo14

    /Users/alamps/AndroidStudioProjects/Demo12SimpleAdapter/Demo12SimpleAdapter/src/main/res/layout/tabl ...

  9. 浅谈JavaScript计时器

    JavaScript计时器 1.什么是JavaScript计时器? 在JavaScript中,我们可以在设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行. 2.计时器类型 一次性计时器:仅在 ...

  10. 夺命雷公狗ThinkPHP项目之----企业网站28之网站前台左侧导航的实现

    我们基于刚才在model层的找顶级分类的代码在进行修改即可: <?php namespace Home\Controller; use Think\Controller; class Commo ...