来自FallDream的博客,未经允许,请勿转载,谢谢。

H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构。伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必修技能。有一天,邪恶的“卡”带着他的邪恶的“常数”来企图毁灭 H 国。“卡”给 H 国的人洗脑说,splay 如果写成单旋的,将会更快。“卡”称“单旋 splay”为“spaly”。虽说他说的很没道理,但还是有 H 国的人相信了,小 H 就是其中之一,spaly 马上成为他的信仰。 而 H 国的国王,自然不允许这样的风气蔓延,国王构造了一组数据,数据由 m 个操作构成,他知道这样的数据肯定打垮 spaly,但是国王还有很多很多其他的事情要做,所以统计每个操作所需要的实际代价的任务就交给你啦。
数据中的操作分为五种:
1. 插入操作:向当前非空 spaly 中插入一个关键码为 key 的新孤立节点。插入方法为,先让 key 和根比较,如果 key 比根小,则往左子树走,否则往右子树走,如此反复,直到某个时刻,key 比当前子树根 x 小,而 x 的左子树为空,那就让 key 成为 x 的左孩子; 或者 key 比当前子树根 x 大,而 x 的右子树为空,那就让 key 成为 
x 的右孩子。该操作的代价为:插入后,key 的深度。特别地,若树为空,则直接让新节点成为一个单个节点的树。(各节点关键码互不相等。对于“深度”的解释见末尾对 spaly 的描述)。
2. 单旋最小值:将 spaly 中关键码最小的元素 xmin 单旋到根。操作代价为:单旋前 xmin 的深度。(对于单旋操作的解释见末尾对 spaly 的描述)。
3. 单旋最大值:将 spaly 中关键码最大的元素 xmax 单旋到根。操作代价为:单旋前 xmax 的深度。
4. 单旋删除最小值:先执行 2 号操作,然后把根删除。由于 2 号操作之后,根没有左子树,所以直接切断根和右子树的联系即可(具体见样例解释)。 操作代价同 2 号操 作。
5. 单旋删除最大值:先执行 3 号操作,然后把根删除。 操作代价同 3 号操作。
对于不是 H 国的人,你可能需要了解一些 spaly 的知识,才能完成国王的任务:
a. spaly 是一棵二叉树,满足对于任意一个节点 x,它如果有左孩子 lx,那么 lx 的关键码小于 x 的关键码。如果有右孩子 rx,那么 rx 的关键码大于 x 的关键码。
b. 一个节点在 spaly 的深度定义为:从根节点到该节点的路径上一共有多少个节点(包括自己)。
c. 单旋操作是对于一棵树上的节点 x 来说的。一开始,设 f 为 x 在树上的父亲。如果 x 为 f 的左孩子,那么执行 zig(x) 操作(如上图中,左边的树经过 zig(x) 变为了右边的树),否则执行 zag(x) 操作(在上图中,将右边的树经过 zag(f) 就变成了左边的树)。每当执 行一次 zig(x) 或者 zag(x),x 的深度减小 1,如此反复,
直到 x 为根。总之,单旋 x 就是通过反复执行 zig 和 zag 将 x 变为根
m<=10^5
很牛逼的一道题233 
只有傻瓜才会写一个spaly去模拟这些操作。
因为这道题只对最值进行操作,所以每次操作全是左旋或者全是右旋
发现把最小值转上去的时候,它的右子树深度不变,其他点深度+1,然后最小值深度变为1
转最大值同理
插入的时候只可能插在前驱右儿子或者后继左儿子。实际上它们只有一个位置是空的,判断插入到哪个即可。
这个明显可以维护。只需要支持区间加,求出最长的大于某一深度的区间,插入一个点,查询前驱后继等操作即可。
因为本题可以离线,所以直接线段树就好了。
当然,既然是一道关于spaly的题,你也可以用splay直接维护哦  复杂度nlogn
#include<iostream>
#include<cstdio>
#define getchar() (*S++)
char B[1<<26],*S=B;
#define INF 2000000000
#define MN 100000
using namespace std;
inline int read()
{int x = 0; char ch = getchar();while(ch < '0' || ch > '9')ch = getchar();while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x;
}int n,fa[MN+5],val[MN+5],cnt=0,c[MN+5][2],rt=0,Q,s[MN+5],dep[MN+5],mn[MN+5],size[MN+5];inline void pushdown(int x)
{int l=c[x][0],r=c[x][1];val[l]+=val[x];dep[l]+=val[x];mn[l]+=val[x];val[r]+=val[x];dep[r]+=val[x];mn[r]+=val[x];val[x]=0;
}inline void update(int x)
{int l=c[x][0],r=c[x][1];size[x]=size[l]+size[r]+1;mn[x]=dep[x];if(l) mn[x]=min(mn[x],mn[l]);if(r) mn[x]=min(mn[x],mn[r]);
}void rotate(int x,int&k)
{int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;if(y==k) k=x; else c[z][c[z][1]==y]=x;fa[x]=z;fa[y]=x;fa[c[x][r]]=y;c[y][l]=c[x][r];c[x][r]=y;update(y);update(x);
}void splay(int x,int&k)
{for(;x!=k;rotate(x,k))if(fa[x]!=k) rotate((c[fa[fa[x]]][1]==fa[x]^c[fa[x]][1]==x)?x:fa[x],k);
}void ins(int&x,int S,int d,int last)
{if(!x){x=++cnt;s[cnt]=S;dep[cnt]=mn[cnt]=d;size[cnt]=1;fa[cnt]=last;return;}ins(c[x][S>s[x]],S,d,x);update(x);
}int Find_Before(int x,int S)
{if(!x) return 0;if(val[x]) pushdown(x);if(s[x]>S) return Find_Before(c[x][0],S);return (Q=Find_Before(c[x][1],S))?Q:x;
}int Find_After(int x,int S)
{if(!x) return 0;if(val[x]) pushdown(x);if(s[x]<S) return Find_After(c[x][1],S);return (Q=Find_After(c[x][0],S))?Q:x;
}int Find(int x,int rk)
{if(val[x]) pushdown(x);int sz=size[c[x][0]]+1;if(sz==rk) return x;if(sz<rk) return Find(c[x][1],rk-sz);return Find(c[x][0],rk);
}int FindLeft(int x,int d)
{if(!x) return 0;if(val[x]) pushdown(x);if(min(mn[c[x][0]],dep[x])>=d) return FindLeft(c[x][1],d)+size[c[x][0]]+1;else return FindLeft(c[x][0],d);
}int FindRight(int x,int d)
{if(!x) return 0;if(val[x]) pushdown(x);if(min(mn[c[x][1]],dep[x])>=d) return FindRight(c[x][0],d)+size[c[x][1]]+1;else return FindRight(c[x][1],d);
}inline int Split(int l,int r)
{int Lt=Find(rt,l-1),Rt=Find(rt,r+1);splay(Lt,rt);splay(Rt,c[rt][1]);return c[c[rt][1]][0];
}inline void Modify(int l,int r,int ad)
{int y=Split(l,r);val[y]+=ad;mn[y]+=ad;dep[y]+=ad;
}void change(int x,int S)
{if(val[x]) pushdown(x);if(s[x]==S) dep[x]=1;else change(c[x][S>s[x]],S);update(x);
}int main()
{fread(B,1,1<<26,stdin);n=read();ins(rt,-INF,INF,0);ins(rt,INF,INF,0);mn[0]=INF;for(int i=1;i<=n;++i){int op=read();if(op==1){int x=read(),bef=Find_Before(rt,x),aft=Find_After(rt,x);int D=max(bef>2?dep[bef]:0,aft>2?dep[aft]:0)+1;ins(rt,x,D,0);splay(cnt,rt);printf("%d\n",D);}if(!(op&1)){int x=Find(rt,2),y=min(FindLeft(rt,dep[x]),size[rt]-1)-1;printf("%d\n",dep[x]);Modify(2,size[rt]-1,1);if(y>1) Modify(2,y+1,-1);change(rt,s[x]);}if((op&1)&&op>1){int x=Find(rt,size[rt]-1),y=min(FindRight(rt,dep[x]),size[rt]-1)-1;printf("%d\n",dep[x]);Modify(2,size[rt]-1,1);if(y>1) Modify(size[rt]-y,size[rt]-1,-1);change(rt,s[x]);}if(op>=4){if(op==4) splay(Find(rt,2),rt);else splay(Find(rt,size[rt]-1),rt);int l=(op==5),r=l^1,y=c[rt][l];c[y][r]=c[rt][r];fa[y]=0;fa[c[rt][r]]=y;rt=y;val[rt]-=1;update(rt);}}return 0;
}

转载于:https://www.cnblogs.com/FallDream/p/bzoj4825.html

[bzoj4825]:[Hnoi2017]单旋相关推荐

  1. [BZOJ4825][HNOI2017]单旋(线段树+Splay)

    4825: [Hnoi2017]单旋 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 667  Solved: 342 [Submit][Status ...

  2. BZOJ4825: [Hnoi2017]单旋(Splay)

    题面 传送门 题解 调了好几个小时--指针太难写了-- 因为只单旋最值,我们以单旋\(\min\)为例,那么\(\min\)是没有左子树的,而它旋到根之后,它的深度变为\(1\),它的右子树里所有节点 ...

  3. [HNOI2017]单旋

    题意: 模拟一棵单旋splay,支持五种操作 \(\text{Solution:}\) 显然不是让你码一颗单旋splay(一条链卡爆你). 在草稿纸上画一画,模拟一遍,观察中序遍历下的深度变化,发现当 ...

  4. 平衡搜索树中的左单旋右单旋双旋

    本文要点: 平衡搜索树的左单旋.右单旋.左右双旋.右左双旋 在平衡搜索树中进行插入结点时,有可能会破坏整棵树的平衡.为了保证平衡不被破坏,就要对一些节点进行旋转,从而来降低树的高度,这样也能保证树的平 ...

  5. 【C++】VAL树的旋转(左单旋、右单旋、双旋)

    AVL树的旋转 在将旋转这一块,最好理解的就是图,所以我主要采取图解的方式,来讲述左单旋.右单旋.以及双旋. 在讲VAL树的旋转之前,先来了解一下VAL树的特性. AVL树的特性 AVL树是一个严格平 ...

  6. Java 平衡二叉树之单旋(左旋,右旋)与双旋

    1.平衡二叉树 平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树, 可以保证查询效率较高. 具有以下特点:它是一 棵空树或它的左右两个 ...

  7. splay单旋与双旋

    在贴吧看了个娱乐向的骗回复帖子,说双旋比单旋慢.好吧,我承认可能在某一次是这样.但别忘了,splay可是平摊时间复杂度的. 那为啥我们要双旋呢?如果你手动模拟四个以上的点的旋转,你会发现,如果单旋一条 ...

  8. JZOJ 7036. 2021.03.30【2021省赛模拟】凌乱平衡树(平衡树单旋+权值线段树)

    JZOJ 7036. 2021.03.30[2021省赛模拟]凌乱平衡树 题目大意 给出两棵Treap,大小分别为 n , m n,m n,m,每个点的 p r i o r i t y priorit ...

  9. Noip前的大抱佛脚----赛前任务

    赛前任务 tags:任务清单 前言 现在xzy太弱了,而且他最近越来越弱了,天天被爆踩,天天被爆踩 题单不会在作业部落发布,所以可(yi)能(ding)会不及时更新 省选前的练习莫名其妙地成为了Noi ...

最新文章

  1. Android和iOS那个好?
  2. 2019年最新最全运维技能图谱
  3. vue依赖缓存_Vue SSR服务端渲染之数据缓存
  4. 【COCOS CREATOR 系列教程之二】脚本开发篇事件监听、常用函数等示例整合
  5. php修改mysql数据库中的表格,如何修改mysql数据库表?
  6. LeetCode 702. 搜索长度未知的有序数组(二分查找)
  7. 25.C++- 泛型编程之函数模板(详解)
  8. oracle delete循环删除_oracle性能优化:高水位线(HWM)详解--如何计算HWM
  9. 有东西,可以倚老卖老,可以倚少卖少
  10. motrix下载没速度_再见迅雷!高颜值没广告的下载神器Motrix体验
  11. 大学计算机考试必备,大学计算机基础大一考试必备题库
  12. 异常值离群点检测算法---箱线图四分位检测
  13. HTML5 页面制作工具
  14. k8s 超详细总结,面试必问
  15. 域组策略与本地组策略
  16. 电脑台式计算机描述不可用,【计算机描述不可用】计算机描述不可用步骤_计算机分级不可用-系统城...
  17. 营销之父科特勒万字演讲实录:营销的未来(含PPT和视频回放)
  18. QVector使用示例
  19. Microsoft PlayReady DRM及其工作原理
  20. 那些年,我们一起参加过的高考

热门文章

  1. 前端基础——day1
  2. springboot整合shiro-关于登出时,redis中缓存没有清理干净的问题
  3. Spring Ioc 之 Bean的加载(1)(生命周期)
  4. 深入理解最大池化为什么能够实现不变性?
  5. C语言模拟实现(一)----- 优先权抢占式时间片调度算法
  6. ANDROID: 超级好用的ADB FORWARD命令
  7. 区块链 PoS 共识——Tendermint
  8. Linux kernel 3.10内核源码分析--进程退出exit_code
  9. JZOJ 5643. 【NOI2018模拟4.10】最小代价
  10. JZOJ 5437. 【NOIP2017提高A组集训10.31】Sequence