4825: [Hnoi2017]单旋

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 667  Solved: 342
[Submit][Status][Discuss]

Description

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 变为根。

Input

第一行单独一个正整数 m。
接下来 m 行,每行描述一个操作:首先是一个操作编号 c∈[1,5],即问题描述中给出的五种操作中的编号,若 c
 = 1,则再输入一个非负整数 key,表示新插入节点的关键码。
1≤m≤10^5,1≤key≤10^9
所有出现的关键码互不相同。任何一个非插入操作,一定保证树非空。在未执行任何操作之前,树为空

Output

输出共 m 行,每行一个整数,第 i 行对应第 i 个输入的操作的代价。

Sample Input

5
1 2
1 1
1 3
4
5

Sample Output

1
2
2
2
2

HINT

 

Source

代码用时:2h

感觉代码难度很低啊为什么我还是花了很长时间。。

思维比较简单,只要把三种操作想清楚了就差不多了。

比较自然的想法应该是Splay,但线段树实现复杂度和常数都更优。

线段树做法网上好像只找到一个(而且没有标记永久化的代码并不是很优美常数也稍大)

这里有一个自认为比较优的做法。

(本题线段树做法的思想还是有点巧妙的,利用了题目只单旋最值的设定)

12140KB,1108ms

  1. #include<set>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<iostream>
  5. #define rep(i,l,r) for (int i=l; i<=r; i++)
  6. using namespace std;
  7.  
  8. template<typename T>inline void rd(T &x){
  9. int t; char ch;
  10. for (t=; !isdigit(ch=getchar()); t=(ch=='-'));
  11. for (x=ch-''; isdigit(ch=getchar()); x=x*+ch-'');
  12. if (t) x=-x;
  13. }
  14.  
  15. const int N=;
  16. int m,tot,d,rt,D[N<<],rs[N],ls[N],fa[N],a[N],b[N],op[N][];
  17. set<int>S;
  18. set<int>::iterator it,sub,pro;
  19.  
  20. void mdf(int x,int L,int R,int l,int r,int k){
  21. if (L==l && R==r){ D[x]+=k; return; }
  22. int mid=(L+R)>>;
  23. if (r<=mid) mdf(x<<,L,mid,l,r,k);
  24. else if (l>mid) mdf((x<<)|,mid+,R,l,r,k);
  25. else mdf(x<<,L,mid,l,mid,k),mdf((x<<)|,mid+,R,mid+,r,k);
  26. }
  27.  
  28. int que(int x,int L,int R,int k){
  29. if (L==R) return D[x];
  30. int mid=(L+R)>>;
  31. if (k<=mid) return D[x]+que(x<<,L,mid,k);
  32. else return D[x]+que((x<<)|,mid+,R,k);
  33. }
  34.  
  35. int main(){
  36. freopen("bzoj4825.in","r",stdin);
  37. freopen("bzoj4825.out","w",stdout);
  38. rd(m);
  39. rep(i,,m){
  40. rd(op[i][]); if (op[i][]==) rd(op[i][]);
  41. if (op[i][]==) b[++tot]=op[i][];
  42. }
  43. sort(b+,b+tot+); tot=unique(b+,b+tot+)-b-;
  44. rep(i,,m) if (op[i][]==) a[i]=lower_bound(b+,b+tot+,op[i][])-b;
  45. rep(i,,m){
  46. if (op[i][]==){
  47. if (S.empty()) rt=a[i],d=;
  48. else{
  49. it=S.upper_bound(a[i]),sub=it;
  50. if (sub==S.begin()) ls[*sub]=a[i],fa[a[i]]=*sub,d=que(,,tot,*sub)+;
  51. else{
  52. pro=--it;
  53. if (sub==S.end()) rs[*pro]=a[i],fa[a[i]]=*pro,d=que(,,tot,*pro)+;
  54. else if (que(,,tot,*sub)<que(,,tot,*pro))
  55. rs[*pro]=a[i],fa[a[i]]=*pro,d=que(,,tot,*pro)+;
  56. else ls[*sub]=a[i],fa[a[i]]=*sub,d=que(,,tot,*sub)+;
  57. }
  58. }
  59. printf("%d\n",d); mdf(,,tot,a[i],a[i],d-que(,,tot,a[i]));
  60. S.insert(a[i]);
  61. }
  62. if (op[i][]== || op[i][]==){
  63. it=S.begin(); d=que(,,tot,*it); printf("%d\n",d);
  64. if ((*it)!=rt){
  65. mdf(,,tot,,tot,); mdf(,,tot,*it,*it,-d);
  66. int t=fa[*it]; ls[t]=fa[*it]=;
  67. if (rs[*it]){
  68. mdf(,,tot,(*it)+,t-,-);
  69. fa[rs[*it]]=t; ls[t]=rs[*it];
  70. }
  71. rs[*it]=rt; fa[rt]=*it; rt=*it;
  72. }
  73. if (op[i][]==) rt=rs[*it],rs[*it]=fa[rt]=,mdf(,,tot,,tot,-),S.erase(*it);
  74. }
  75. if (op[i][]== || op[i][]==){
  76. it=S.end(); it--; d=que(,,tot,*it); printf("%d\n",d);
  77. if ((*it)!=rt){
  78. mdf(,,tot,,tot,); mdf(,,tot,*it,*it,-d);
  79. int t=fa[*it]; rs[t]=fa[*it]=;
  80. if (ls[*it]){
  81. mdf(,,tot,t+,(*it)-,-);
  82. fa[ls[*it]]=t; rs[t]=ls[*it];
  83. }
  84. ls[*it]=rt; fa[rt]=*it; rt=*it;
  85. }
  86. if (op[i][]==) rt=ls[*it],ls[*it]=fa[rt]=,mdf(,,tot,,tot,-),S.erase(*it);
  87. }
  88. }
  89. return ;
  90. }

惊了,splay跑的比线段树还快。

5752KB,900ms

代码用时:1h

一开始想自己写出来,思路并不复杂但代码很繁琐(模板占了大部分但想敲对并不容易)。

看了网上的代码抄了一份下来竟然1A,可能是我抄代码的经历中最顺利的一次吧。

但抄来的代码终究不是自己的,要勇于写这种代码稍长的题目,后面还会有比这长的多的题!

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. #define ls c[x][0]
  5. #define rs c[x][1]
  6. #define rep(i,l,r) for (int i=l; i<=r; i++)
  7. using namespace std;
  8.  
  9. template<typename T>inline void rd(T &x){
  10. int t; char ch;
  11. for (t=; !isdigit(ch=getchar()); t=(ch=='-'));
  12. for (x=ch-''; isdigit(ch=getchar()); x=x*+ch-'');
  13. if (t) x=-x;
  14. }
  15.  
  16. const int inf=,N=;
  17. int n,cnt,rt,Q,fa[N],v[N],sz[N],c[N][],s[N],dep[N],mn[N];
  18.  
  19. void push(int x){
  20. v[ls]+=v[x]; dep[ls]+=v[x]; mn[ls]+=v[x];
  21. v[rs]+=v[x]; dep[rs]+=v[x]; mn[rs]+=v[x];
  22. v[x]=;
  23. }
  24.  
  25. void upd(int x){
  26. sz[x]=sz[ls]+sz[rs]+; mn[x]=dep[x];
  27. if (ls) mn[x]=min(mn[x],mn[ls]);
  28. if (rs) mn[x]=min(mn[x],mn[rs]);
  29. }
  30.  
  31. void rot(int &rt,int x){
  32. int y=fa[x],z=fa[y],w=(c[y][]==x);
  33. if (y==rt) rt=x; else c[z][c[z][]==y]=x;
  34. fa[x]=z; fa[y]=x; fa[c[x][w^]]=y;
  35. c[y][w]=c[x][w^]; c[x][w^]=y; upd(y);
  36. }
  37.  
  38. void splay(int &rt,int x){
  39. while (x!=rt) {
  40. int y=fa[x],z=fa[y];
  41. if (y!=rt){
  42. if ((c[z][]==y)^(c[y][]==x)) rot(rt,x); else rot(rt,y);
  43. }
  44. rot(rt,x);
  45. }
  46. upd(x);
  47. }
  48.  
  49. void ins(int &x,int S,int d,int lst){
  50. if (!x){
  51. x=++cnt, s[cnt]=S; dep[cnt]=mn[cnt]=d;
  52. sz[cnt]=; fa[cnt]=lst; return;
  53. }
  54. ins(c[x][S>s[x]],S,d,x); upd(x);
  55. }
  56.  
  57. int getpre(int x,int S){
  58. if (!x) return ;
  59. if (v[x]) push(x);
  60. if (s[x]>S) return getpre(c[x][],S);
  61. Q=getpre(c[x][],S);
  62. if (Q) return Q; else return x;
  63. }
  64.  
  65. int getnxt(int x,int S){
  66. if (!x) return ;
  67. if (v[x]) push(x);
  68. if (s[x]<S) return getnxt(rs,S);
  69. Q=getnxt(ls,S);
  70. if (Q) return Q; else return x;
  71. }
  72.  
  73. int find(int x,int k){
  74. if (v[x]) push(x);
  75. if (sz[ls]+==k) return x;
  76. if (sz[ls]+<k) return find(rs,k-sz[ls]-);
  77. return find(ls,k);
  78. }
  79.  
  80. int getl(int x,int d){
  81. if (!x) return ;
  82. if (v[x]) push(x);
  83. if (min(mn[ls],dep[x])>=d) return getl(rs,d)+sz[ls]+;
  84. else return getl(ls,d);
  85. }
  86.  
  87. int getr(int x,int d){
  88. if (!x) return ;
  89. if (v[x]) push(x);
  90. if (min(mn[rs],dep[x])>=d) return getr(ls,d)+sz[rs]+;
  91. else return getr(rs,d);
  92. }
  93.  
  94. int split(int l,int r){
  95. int t1=find(rt,l-),t2=find(rt,r+);
  96. splay(rt,t1); splay(c[rt][],t2); return c[c[rt][]][];
  97. }
  98.  
  99. void mdf(int l,int r,int ad){
  100. int y=split(l,r); v[y]+=ad; mn[y]+=ad; dep[y]+=ad;
  101. }
  102.  
  103. void change(int x,int S){
  104. if (v[x]) push(x);
  105. if (s[x]==S) dep[x]=; else change(c[x][S>s[x]],S);
  106. upd(x);
  107. }
  108.  
  109. int main(){
  110. freopen("bzoj4825.in","r",stdin);
  111. freopen("bzoj4825.out","w",stdout);
  112. rd(n); ins(rt,-inf,inf,); ins(rt,inf,inf,); mn[]=inf;
  113. rep(i,,n){
  114. int op,x; rd(op);
  115. if (op==){
  116. rd(x); int t1=getpre(rt,x),t2=getnxt(rt,x);
  117. int D=max(t1> ? dep[t1] : ,t2> ? dep[t2] : )+;
  118. ins(rt,x,D,); splay(rt,cnt); printf("%d\n",D);
  119. }
  120. if (!(op&)){
  121. int x=find(rt,),y=min(getl(rt,dep[x]),sz[rt]-)-;
  122. printf("%d\n",dep[x]); mdf(,sz[rt]-,);
  123. if (y>) mdf(,y+,-);
  124. change(rt,s[x]);
  125. }
  126. if ((op&)&&(op>)){
  127. int x=find(rt,sz[rt]-),y=min(getr(rt,dep[x]),sz[rt]-)-;
  128. printf("%d\n",dep[x]); mdf(,sz[rt]-,);
  129. if (y>) mdf(sz[rt]-y,sz[rt]-,-);
  130. change(rt,s[x]);
  131. }
  132. if (op>=){
  133. if (op==) splay(rt,find(rt,));
  134. else splay(rt,find(rt,sz[rt]-));
  135. int l=(op==),r=l^,y=c[rt][l];
  136. c[y][r]=c[rt][r]; fa[y]=; fa[c[rt][r]]=y; rt=y;
  137. v[rt]-=; upd(rt);
  138. }
  139. }
  140. return ;
  141. }

[BZOJ4825][HNOI2017]单旋(线段树+Splay)的更多相关文章

  1. 【BZOJ4825】[Hnoi2017]单旋 线段树+set

    [BZOJ4825][Hnoi2017]单旋 Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能 ...

  2. 【bzoj4825】[Hnoi2017]单旋 线段树+STL-set

    题目描述 H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必修技能.有一天 ...

  3. BZOJ.4825.[AHOI/HNOI2017]单旋(线段树)

    BZOJ LOJ 洛谷 这题不难啊,我怎么就那么傻,拿随便一个节点去模拟.. 我们只需要能够维护,将最小值或最大值转到根.模拟一下发现,对于最小值,它的右子树深度不变(如果存在),其余节点深度全部\( ...

  4. 洛谷P3721 [AH2017/HNOI2017]单旋(线段树 set spaly)

    题意 题目链接 Sol 这题好毒瘤啊.. 首先要观察到几个性质: 将最小值旋转到根相当于把右子树变为祖先的左子树,然后将原来的根变为当前最小值 上述操作对深度的影响相当于右子树不变,其他的位置-1 然 ...

  5. BZOJ4825 [Hnoi2017]单旋 【线段树】

    题目链接 BZOJ4825 题解 手模一下操作,会发现一些很优美的性质: 每次旋到根,只有其子树深度不变,剩余点深度\(+1\) 每次旋到根,[最小值为例]右儿子接到其父亲的左儿子,其余点形态不改变, ...

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

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

  7. bzoj4825 [Hnoi2017]单旋

    Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必 ...

  8. [BZOJ4825][HNOI2017]单旋spaly

    BZOJ Luogu 题目太长了,就不放了. 题解 首先声明一点,无论是splay还是spaly,插入一个新的元素,都要rotate到根!所以说题目也算是给了一个错误示范吧. 我们发现把最值旋转到根并 ...

  9. 4825: [Hnoi2017]单旋

    4825: [Hnoi2017]单旋 链接 分析: 以后采取更保险的方式写代码!!!81行本来以为不特判也可以,然后就总是比答案大1,甚至出现负数,调啊调啊调啊调~~~ 只会旋转最大值和最小值,以最小 ...

随机推荐

  1. Broken Necklace

    Description 你有一条由N个红色的,白色的,或蓝色的珠子组成的项链(3<=N<=350),珠子是随意安排的. 这里是 n=29 的二个 例子: 1 2 1 2 r b b r b ...

  2. Windows Live Writer博客草稿迁移的一种解决方案

    作为一个苦逼的码农,喜欢写博客做总结是很正常的事,写博客写的久的人都接触过各种客户端工具,最流行的就是Windows Live Writer了. 作为一个苦逼的码农,换电脑也是很经常的事,经常会出现一 ...

  3. CRF原理解读

    概率有向图又称为贝叶斯网络,概率无向图又称为马尔科夫网络.具体地,他们的核心差异表现在如何求  ,即怎么表示  这个的联合概率. 概率图模型的优点: 提供了一个简单的方式将概率模型的结构可视化. 通过 ...

  4. Coursera在线学习---第二节.Octave学习

    1)两个矩阵相乘 A*B 2)两个矩阵元素位相乘(A.B矩阵中对应位置的元素相乘) A.*B 3)矩阵A的元素进行平方 A.^2 4)向量或矩阵中的元素求倒数 1./V    或   1./A 5) ...

  5. perl6正则 6: 大小写/空白/匹配所有符合

    这个 :g 只能写在外面 m:g /re/

  6. 5-python的封装与结构 - set集合

    目录 1 封装与解构 1.1 封装 1.2 解构 1.3 Python3的解构 2 set类型 2.1 set的定义 2.2 set的基本操作 2.2.1 增加元素 2.2.2 删除元素 2.2.3 ...

  7. sicily 1240. Faulty Odometer

    Description You are given a car odometer which displays the miles traveled as an integer. The odomet ...

  8. windows安装React Native开发运行环境

    React Native是什么 React Native是facebook开源的一个用于开发app的框架.React Native的设计理念:既拥有Native (原生) 的用户体验.又保留React ...

  9. u-boot界面添加命令[demo]

    目标板:2440 如何在u-boot界面中增加命令 在/common/目录下建立文件,调用执行函数do_bootm就行,然后在修改Makefile,就OK了. 比如在u-boot界面添加命令test ...

  10. Linux /etc/cron.d作用(转自 定时任务crontab cron.d)

    原文链接:http://huangfuligang.blog.51cto.com/9181639/1608549 一.cron.d增加定时任务 当我们要增加全局性的计划任务时,一种方式是直接修改/et ...