[HNOI2017]单旋

题目描述

H国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构。伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了H国的必修技能。有一天,邪恶的“卡”带着他的邪恶的“常数”来企图毁灭H国。“卡”给H国的人洗脑说,splay如果写成单旋的,将会更快。“卡”称“单旋splay”为“spaly”。虽说他说的很没道理,但还是有H国的人相信了,小H就是其中之一,spaly马上成为他的信仰。而H国的国王,自然不允许这样的风气蔓延,国王构造了一组数据,数据由m(不超过10^5)个操作构成,他知道这样的数据肯定打垮spaly,但是国王还有很多很多其他的事情要做,所以统计每个操作

所需要的实际代价的任务就交给你啦。数据中的操作分为5种:

  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的知识,才能完成国王的任务:

  1. spaly是一棵二叉树,满足对于任意一个节点x,它如果有左孩子lx,那么lx的关键码小于x的关键码。如果有右孩子rx,那么rx的关键码大于x的关键码。

  2. 一个节点在spaly的深度定义为:从根节点到该节点的路径上一共有多少个节点(包括自己)。

  3. 单旋操作是对于一棵树上的节点x来说的。一开始,设f为x在树上的父亲。如果x为f的左孩子,那么执行zig(x)操作(如上图中,左边的树经过zig(x)变为了右边的树),否则执行zag(x)操作(在上图中,将右边的树经过zag(f)就变成了左边的树)。每当执行一次zig(x)或者zag(x),x的深度减小1,如此反复,直到x为根。总之,单旋x就是通过反复执行zig和zag将x变为根。

输入输出格式

输入格式:

 

输入文件名为 splay.in。

第一行单独一个正整数 m (1 <= m <= 10^5)。

接下来 m 行,每行描述一个操作:首先是一个操作编号 c( 1<=c<=5),既问题描述中给出的 5 种操作中的编号,若 c= 1,则再输入一个非负整数 key,表示新插入节点的关键码。

 

输出格式:

 

输出文件名为 splay.out。

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

 

输入输出样例

输入样例#1:
5
1 2
1 1
1 3
4 
5
输出样例#1:
1 
2 
2
2 
2

说明

20%的数据满足: 1 <= m <= 1000。

另外 30%的数据满足: 不存在 4,5 操作。

100%的数据满足: 1<=m<=10^5; 1<=key<=10^9。 所有出现的关键码互不相同。 任何一个非插入操作,一定保证树非空。 在未执行任何操作之前,树为空。

 

 

没想到这个spaly有这么多鬼畜的性质。
先手玩模拟一下会有巨大的发现.每次单旋最小值,整个树都只会有细微的变化,假如当前的最小值是u,那么会变化的地方只有:u有右儿子,那么u的右儿子会变成fa[u]的左儿子.u会变成根,原来的根变成u的右儿子.单旋最大值相反.
有了这个性质,我们就可以维护一颗LCT,每次把要切的边切掉,要连的边连上.
维护一个set用来快速查找要插入的位置和最小()值的位置.每次插入时,先找到这个点在set中的前驱和后继,因为前驱的右儿子和后继的左儿子有且只有一个是空的,那么插入那个空的就可以了.
每次查询深度的时候就先把这个点access一下,然后查询这个splay中的大小.
具体实现还要维护一下每个权值对应的在原树中的节点编号,我偷懒用了个map,慢的飞起.
关于维护树的形态,我又用了一颗树,这样还比较好写,每次修改的时候修改形态树和LCT.
  1 #include<bits/stdc++.h>
  2 #define maxn 100010
  3 #define ls t[x].ch[0]
  4 #define rs t[x].ch[1]
  5 #define pre t[x].fa
  6 #define inf 1000000001
  7 using namespace std;
  8 set<int>st;
  9 map<int,int>mp;
 10 int ch[maxn][2],fa[maxn],tot=0,rt=0,key[maxn];
 11 struct data{int ch[2],fa,sum,lz;}t[maxn];
 12 inline bool isrt(int x){return t[pre].ch[0]!=x && t[pre].ch[1]!=x;}
 13 inline bool son(int x){return t[pre].ch[1]==x;}
 14 inline void updata(int x){t[x].sum=t[ls].sum+t[rs].sum+1;}
 15 inline void down(int x){
 16     if(t[x].lz==1)
 17       swap(ls,rs),t[ls].lz^=1,t[rs].lz^=1,t[x].lz=0;
 18   }
 19 inline void pd(int x){if(!isrt(x)) pd(pre);down(x);}
 20 inline void Rotate(int x){
 21     int f=t[x].fa,g=t[f].fa,c=son(x);
 22     if(!isrt(f)) t[g].ch[son(f)]=x;
 23     t[x].fa=g;
 24     t[f].ch[c]=t[x].ch[c^1],t[t[f].ch[c]].fa=f;
 25     t[x].ch[c^1]=f,t[f].fa=x;
 26     updata(f),updata(x);
 27   }
 28 inline void Splay(int x){
 29     pd(x);
 30     for(;!isrt(x);Rotate(x))
 31       if(!isrt(pre)) Rotate(son(pre)==son(x)?pre:x);
 32   }
 33 inline void access(int x){
 34   for(int y=0;x;y=x,x=pre) Splay(x),rs=y,updata(x);
 35 }
 36 inline void makert(int x){
 37   access(x),Splay(x),t[x].lz^=1;
 38 }
 39 inline void link(int x,int y){
 40   makert(x);t[x].fa=y;
 41 }
 42 inline void cut(int x,int y){
 43   makert(x),access(y),Splay(y);
 44   t[x].fa=t[y].ch[0]=0;
 45   updata(y);
 46 }
 47 inline void getdeep(int x){
 48   pd(x);
 49   access(x);
 50   Splay(x);
 51   printf("%d\n",t[x].sum);
 52 }
 53 inline void newnode(int keyy){
 54   st.insert(keyy);
 55   int pr=*--st.find(keyy);
 56   int nex=*++st.find(keyy);
 57   mp[keyy]=++tot;
 58   key[tot]=keyy;
 59   if(pr==-1 && nex==inf)rt=tot,printf("1\n");
 60   else if(pr==-1) fa[tot]=mp[nex],ch[mp[nex]][0]=tot,link(tot,mp[nex]),getdeep(tot);
 61   else if(nex==inf) fa[tot]=mp[pr],ch[mp[pr]][1]=tot,link(tot,mp[pr]),getdeep(tot);
 62   else{
 63     int t1=mp[pr],t2=mp[nex];
 64     if(!ch[t1][1]) ch[t1][1]=tot,fa[tot]=t1,link(tot,t1),getdeep(tot);
 65     if(!ch[t2][0]) ch[t2][0]=tot,fa[tot]=t2,link(tot,t2),getdeep(tot);
 66   }
 67 }
 68 inline void rotatemin(){
 69   int u=mp[*++st.find(-1)];
 70   if(u==rt){printf("1\n");return;}
 71   getdeep(u);
 72   cut(u,fa[u]);ch[fa[u]][0]=0;
 73   if(ch[u][1])cut(u,ch[u][1]),link(ch[u][1],fa[u]),ch[fa[u]][0]=ch[u][1],fa[ch[u][1]]=fa[u];
 74   link(rt,u);
 75   fa[u]=0,ch[u][1]=rt,fa[rt]=u;
 76   rt=u;
 77 }
 78 inline void rotatemax(){
 79   int u=mp[*--st.find(inf)];
 80   if(u==rt){printf("1\n");return;}
 81   getdeep(u);
 82   cut(u,fa[u]);ch[fa[u]][1]=0;
 83   if(ch[u][0])cut(u,ch[u][0]),link(ch[u][0],fa[u]),ch[fa[u]][1]=ch[u][0],fa[ch[u][0]]=fa[u];
 84   link(rt,u);
 85   fa[u]=0,ch[u][0]=rt,fa[rt]=u;
 86   rt=u;
 87 }
 88 int main(){
 89   freopen("!.in","r",stdin);
 90   freopen("!.out","w",stdout);
 91   int n,type,x;
 92   scanf("%d",&n);
 93   st.insert(-1),st.insert(inf);
 94   for(int i=1;i<=n;i++){
 95     if(i==106){
 96       i=106;
 97     }
 98     scanf("%d",&type);
 99     if(type==1)scanf("%d",&x),newnode(x);
100     if(type==2)rotatemin();
101     if(type==3)rotatemax();
102     if(type==4)rotatemin(),cut(rt,ch[rt][1]),st.erase(key[rt]),fa[ch[rt][1]]=0,rt=ch[rt][1];
103     if(type==5)rotatemax(),cut(rt,ch[rt][0]),st.erase(key[rt]),fa[ch[rt][0]]=0,rt=ch[rt][0];
104   }
105   return 0;
106 }
 
   

 

 

转载于:https://www.cnblogs.com/pantakill/p/7282186.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值