【非旋treap】 fhq-treap总结及模板

各位大佬讲得真好   我既然讲不好就不讲了

http://www.cnblogs.com/mjtcn/p/8028926.html

https://blog.csdn.net/cabi_zgx/article/details/79963427

https://www.luogu.org/blog/Chanis/fhq-treap

https://www.cnblogs.com/BCOI/p/9072444.html

非旋treap常数大 

【模板】普通平衡树

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入x数
  2. 删除x数(若有多个相同的数,因只删除一个)
  3. 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
  4. 查询排名为x的数
  5. 求x的前驱(前驱定义为小于x,且最大的数)
  6. 求x的后继(后继定义为大于x,且最小的数)

输入输出格式

输入格式:

第一行为n,表示操作的个数,下面nn行每行有两个数opt和x,opt表示操作的序号(1≤opt≤6 )

输出格式:

对于操作3,4,5,6每行输出一个数,表示对应答案

输入输出样例

输入样例#1: 

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

输出样例#1:

106465
84185
492737

说明

时空限制:1000ms,128M

1.n的数据范围: n≤100000

2.每个数的数据范围: [-10^7, 10^7]

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=5*100000+5;
int num,root,ch[N][2],val[N],rnd[N],siz[N];

inline void pushup(int x) 
  {siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;}
  
inline int  make(int x) 
 {val[++num]=x; rnd[num]=rand(); siz[num]=1; 
  return num;
 }
 
void split(int now,int k,int &x,int &y)
 {if(!now) x=y=0;
  else 
     {if(val[now]<=k)
        {x=now; split(ch[now][1],k,ch[now][1],y);
        }
      else 
        {y=now; split(ch[now][0],k,x,ch[now][0]);
        }
      pushup(now);  
     }
 }
 
int merge(int x,int y) 
 {if(!x || !y) return x+y;
 
  if(rnd[x]<=rnd[y])
    {ch[x][1]=merge(ch[x][1],y);
     pushup(x); return x;
    }
  else 
    {ch[y][0]=merge(x,ch[y][0]);
     pushup(y); return y;
    }  
 }
 
inline int getkth(int p,int k)
 {while(1)
   {if(k==siz[ch[p][0]]+1) return p;
    if(ch[p][0] && k<=siz[ch[p][0]]) p=ch[p][0];
    else {k=k-siz[ch[p][0]]-1;
          p=ch[p][1];
         }
   }
 } 
int main()
{int m,op,k,x,y,z; scanf("%d",&m);	
    
 while(m--)	
  {scanf("%d%d",&op,&k);
    if(op==1)	
  	 {split(root,k,x,y);
  	  root=merge(merge(x,make(k)),y);
  	 }
   else	if(op==2)	
  	 {split(root,k,x,y);
  	  split(x,k-1,x,z);
  	  z=merge(ch[z][0],ch[z][1]);
  	  root=merge(merge(x,z),y);
  	 }
   else	if(op==3)	
  	 {split(root,k-1,x,y);
  	  printf("%d\n",siz[x]+1);
  	  root=merge(x,y);
  	 }
   else	if(op==4)	
  	 {printf("%d\n",val[getkth(root,k)]);
  	 }
   else	if(op==5)	
  	 {split(root,k-1,x,y);
  	  printf("%d\n",val[getkth(x,siz[x])]);
  	  root=merge(x,y);
  	 }
   else	if(op==6)	
  	 {split(root,k,x,y);
  	  printf("%d\n",val[getkth(y,1)]);
  	  root=merge(x,y);
  	 }  
  }	
return 0;
}

BZOJ1251: 序列终结者

Description

网上有许多题,就是给定一个序列,要你支持几种操作:A、B、C、D。一看另一道题,又是一个序列 要支持几种操作:D、C、B、A。尤其是我们这里的某人,出模拟试题,居然还出了一道这样的,真是没技术含量……这样 我也出一道题,我出这一道的目的是为了让大家以后做这种题目有一个“库”可以依靠,没有什么其他的意思。这道题目 就叫序列终结者吧。 【问题描述】 给定一个长度为N的序列,每个序列的元素是一个整数(废话)。要支持以下三种操作: 1. 将[L,R]这个区间内的所有数加上V。 2. 将[L,R]这个区间翻转,比如1 2 3 4变成4 3 2 1。 3. 求[L,R]这个区间中的最大值。 最开始所有元素都是0。

Input

第一行两个整数N,M。M为操作个数。 以下M行,每行最多四个整数,依次为K,L,R,V。K表示是第几种操作,如果不是第1种操作则K后面只有两个数。

Output

对于每个第3种操作,给出正确的回答。

Sample Input

4 4
1 1 3 2
1 2 4 -1
2 1 3
3 2 4

Sample Output

2
【数据范围】
N<=50000,M<=100000。

重点----0号节点的初始化 

否则有一个儿子为空的时候pushup 负数时会导致max值为不存在的0

 

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=50000+5; const int INF=0x3f3f3f;
int n,root,num,ls[N],rs[N],rnd[N],siz[N],val[N],add[N],maxx[N]; bool rev[N];
inline int make(int x)
 {rnd[++num]=rand(); val[num]=maxx[num]=x; siz[num]=1; return num;}
 
inline void pass(int i,long long v)
 {if(!i) return;
  add[i]+=v; maxx[i]+=v; val[i]+=v;
 } 
inline void pushup(int i)
 {siz[i]=siz[ls[i]]+siz[rs[i]]+1;
  maxx[i]=max(val[i],max(maxx[ls[i]],maxx[rs[i]]));
 } 
 
inline void pushdown(int i)
 {if(!i) return;
  if(rev[i])
   {rev[i]=0;
    swap(ls[i],rs[i]);
    if(ls[i]) rev[ls[i]]^=1;
    if(rs[i]) rev[rs[i]]^=1; 
   }
  
  if(add[i])
   {pass(ls[i],add[i]);  
    pass(rs[i],add[i]);
    
    add[i]=0;
   } 
 } 
 
void split(int now,int k,int &x,int &y)
 {if(!now) {x=y=0;return;}
  pushdown(now);
  if(k<=siz[ls[now]])
    {y=now; split(ls[now],k,x,ls[now]);
    }
  else 
    {x=now; split(rs[now],k-siz[ls[now]]-1,rs[now],y);
    }  
  pushup(now);  
 } 

int merge(int x,int y)
 {if(!x || !y) return x+y;
 
  pushdown(x); pushdown(y); 
  if(rnd[x]<=rnd[y])
    {rs[x]=merge(rs[x],y);
     pushup(x); return x;
    }
  else 
    {ls[y]=merge(x,ls[y]);
     pushup(y); return y;
    }  
 } /*
void print(int x)
 {if(!x) return;
  pushdown(x);
  print(ls[x]);
  printf("%d ",val[x]);
  print(rs[x]);
 }*/

int main()
{
 int m,l,r,op,x,y,z,v; scanf("%d%d",&n,&m);	
 
 maxx[0]=-INF;  //0号节点初始化
 
 while(n--) {root=merge(root,make(0));}	
	
 while(m--)	
  {scanf("%d%d%d",&op,&l,&r);
  
   split(root,r,x,y);
   split(x,l-1,x,z);
   
   if(op==1){scanf("%d",&v); pass(z,v); }
   else if(op==2) rev[z]^=1;
   else           printf("%d\n",maxx[z]);
     
   root=merge(merge(x,z),y);
  // print(root); printf("\n");
  }		
return 0;
}

 

转载于:https://www.cnblogs.com/YuXiaoze/p/10679519.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值