一、题目

UVA11922

二、分析

为什么会有伸展树?

伸展树与AVL的区别除了保持平衡的方式不同外,最重要的是在每次查找点时,让该点旋转到根结点,这里可以结合计算机里的局部性原理思考。

伸展树有什么优势?

有了伸展树,我们可以根据每次到根节点的值,根据二叉搜索树的性质,可以将整棵树划分成两个部分,左子树的值都比根结点值大,右子树的值都比根结点小。

该题除了伸展树还用到了什么?

该题还需要旋转,所以,需要像线段树的lazy标记一样标记是否需要旋转。

有什么需要注意的地方?

一定注意写法的不同,构建树的范围不同,如果没有定于null数组,则需要将建树范围从$[1,n]$扩为$[0,n]$,因为空指针的sum(表示以该结点为根的树的大小)是不清楚的。

三、AC代码

 #include <cstdio>
#include <cstring>
#include <algorithm> using namespace std; struct Node
{
Node *ch[];
int sum, val;
int flip;
Node(int v = )
{
val = v;
sum = ;
flip = ;
ch[] = ch[] = NULL;
}
int cmp(int x)
{
int d = x - (ch[] == NULL ? : ch[]->sum);
if(d == ) return -;
else return d <= ? : ;
}
void maintain()
{
sum = ;
if(ch[] != NULL) sum += ch[]->sum;
if(ch[] != NULL) sum += ch[]->sum;
}
void pushdown()
{
if(flip)
{
flip = ;
swap(ch[], ch[]); //**
if(ch[] != NULL)
ch[]->flip = !ch[]->flip;
if(ch[] != NULL)
ch[]->flip = !ch[]->flip;
}
}
}; void build(Node* &o, int l, int r)
{
if(l > r)
return;
int mid = (l + r) >> ;
o = new Node(mid);
build(o->ch[], l, mid - );
build(o->ch[], mid + , r);
o->maintain();
} void rotate(Node* &o, int d)
{
Node *k = o->ch[d^];
o->ch[d^] = k->ch[d];
k->ch[d] = o;
o->maintain();
k->maintain();
o = k;
} void splay(Node* &o, int k)
{
o->pushdown();
int d = o->cmp(k);
if(d == )
k -= (o->ch[] == NULL ? : o->ch[]->sum) + ;
//当前结点不是第k个,则需要往上伸展
if(d != -)
{
Node *p = o->ch[d];
p->pushdown(); int d2 = p->cmp(k);
int k2 = (d2 == ? k : k - (p->ch[] == NULL ? :p->ch[]->sum) - );
if(d2 != -)
{
splay(p->ch[d2], k2);
//x,x的父节点,x祖父结点三点共线
if(d == d2) rotate(o, d^);
//不共线
else rotate(o->ch[d], d);
}
rotate(o, d^);
}
} void print(Node* &o) //一定要传引用
{
o->pushdown();
if(o->ch[] != NULL) print(o->ch[]);
if(o->val)
printf("%d\n", o->val);
if(o->ch[] != NULL) print(o->ch[]); delete o;
o = NULL;
} Node* merge(Node* left, Node* right)
{
//left不能为NULL
//因为初始化了sum为1,对于空指针的sum,不清楚大小
//如何避免:建树从0开始,即保证sum>0
splay(left, left->sum);
left->ch[] = right;
left->maintain();
return left;
} void split(Node *o, int k, Node* &left, Node* &right)
{
splay(o, k);
left = o;
right = o->ch[];
o->ch[] = NULL;
left->maintain();
} int main()
{
//freopen("input.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int N, M, a, b;
while(scanf("%d %d", &N, &M)==)
{
Node *root = NULL;
Node *left, *mid, *right, *o;
//上述模板注意不能用空指针
//必须从0开始,不然会RE
build(root, , N); for(int i = ; i < M; i++)
{
scanf("%d %d", &a, &b);
//树里面有0所以是a
split(root, a, left, o);
split(o, b - a + , mid, right);
mid->flip ^= ;
root = merge(merge(left, right), mid);
}
print(root);
}
return ;
}

UVA_11922 Permutation Transformer 【splay树】的更多相关文章

  1. UVA 11922 Permutation Transformer —— splay伸展树

    题意:根据m条指令改变排列1 2 3 4 … n ,每条指令(a, b)表示取出第a~b个元素,反转后添加到排列尾部 分析:用一个可分裂合并的序列来表示整个序列,截取一段可以用两次分裂一次合并实现,粘 ...

  2. UVa 11922 - Permutation Transformer 伸展树

    第一棵伸展树,各种调试模板……TVT 对于 1 n 这种查询我处理的不太好,之前序列前后没有添加冗余节点,一直Runtime Error. 后来加上冗余节点之后又出了别的状况,因为多了 0 和 n+1 ...

  3. UVA - 11922 Permutation Transformer (splay)

    题目链接 题意:你的任务是根据m条指令改变排列{!,2,3,...,n}.每条指令(a,b)表示取出第a~b个元素,翻转后添加到排列的尾部.输出最终序列. 解法:splay对区间分裂合并翻转,模板题. ...

  4. Splay树-Codevs 1296 营业额统计

    Codevs 1296 营业额统计 题目描述 Description Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司 ...

  5. ZOJ3765 Lights Splay树

    非常裸的一棵Splay树,需要询问的是区间gcd,但是区间上每个数分成了两种状态,做的时候分别存在val[2]的数组里就好.区间gcd的时候基本上不支持区间的操作了吧..不然你一个区间里加一个数gcd ...

  6. Splay树再学习

    队友最近可能在学Splay,然后让我敲下HDU1754的题,其实是很裸的一个线段树,不过用下Splay也无妨,他说他双旋超时,单旋过了,所以我就敲来看下.但是之前写的那个Splay越发的觉得不能看,所 ...

  7. 暑假学习日记:Splay树

    从昨天开始我就想学这个伸展树了,今天花了一个上午2个多小时加下午2个多小时,学习了一下伸展树(Splay树),学习的时候主要是看别人博客啦~发现下面这个博客挺不错的http://zakir.is-pr ...

  8. 1439. Battle with You-Know-Who(splay树)

    1439 路漫漫其修远兮~ 手抄一枚splay树 长长的模版.. 关于spaly树的讲解   网上很多随手贴一篇 貌似这题可以用什么bst啦 堆啦 平衡树啦 等等 这些本质都是有共同点的 查找.删除特 ...

  9. 伸展树(Splay树)的简要操作

    伸展树(splay树),是二叉排序树的一种.[两个月之前写过,今天突然想写个博客...] 伸展树和一般的二叉排序树不同的是,在每次执行完插入.查询.删除等操作后,都会自动平衡这棵树.(说是自动,也就是 ...

随机推荐

  1. 一些使用的linux库

    1.curl网络库 apt-get install libcurl4-openssl-dev sudo apt-get install curl 2.jsoncpp库 sudo apt-get ins ...

  2. UVa 12093 Protecting Zonk (树形DP)

    题意:给定一个有n个节点的无根树,有两种装置A和B,每种都有无限多个.在某个节点X使用A装置需要C1的花费,并且此时与节点X相连的边都被覆盖.在某个节点X使用B装置需要C2的花费,并且此时与节点X相连 ...

  3. 搭建简易Web GIS网站:使用GeoServer+PostgreSQL+PostGIS+OpenLayers3

    Web GIS系列: 搭建简易Web GIS网站:使用GeoServer+PostgreSQL+PostGIS+OpenLayers3 使用GeoServer+QGIS发布WMTS服务 使用GeoSe ...

  4. 打开窗口进行选择文件(txt文件),打开所选文件,读入文件

    用mfc编写项目的时候往往需要调用窗口,允许用户通过窗口进行选择文件操作 TCHAR szBuffer[MAX_PATH] = { 0 }; OPENFILENAME ofn = { 0 }; ofn ...

  5. awk基础04-内置函数

        在awk中常用的内置函数大概分为:数值函数.字符函数.时间函数.二进制操作函数.数组函数.自定义函数等. 数值函数   常用的数值函数主要有int.rand.srand.sqrt等.详细如下所 ...

  6. POJ1087 A Plug for UNIX 2017-02-12 13:38 40人阅读 评论(0) 收藏

    A Plug for UNIX Description You are in charge of setting up the press room for the inaugural meeting ...

  7. kali下搭建WiFi钓鱼热点

    在linux下建立无线热点并不像在windows下开启网络共享或者使用无线网卡驱动设置AP模式即可. linux下的无线共享要用到两个软件:hostapd(创建无线热点).dnsmasq(dns服务和 ...

  8. [Postgres]Postgres复制表

    在需要把含有分表的总表备份的时候想到的笨办法,如果有什么更先进的办法万望告知. 比如TableOld是由TableOld1,TableOld2,TableOld3组合而成,现在需要对TableOld进 ...

  9. SEGGER J-Link install

    Why J-Link? In case you wonder why GNU ARM Eclipse decided to provide support to SEGGER J-Link, the ...

  10. 10-06 Linux的基本命令以及一些简单的通配符说明

    Shell的通配符 主要用于模式匹配,如:文件名匹配,路径名搜索,字符查找等.常用的有:'*','?','[]' '*':代表任意长度的字串. '?':代表单个任意字符 '[]':代表模式串匹配的字符 ...