刷题总结——二逼平衡树(bzoj3224线段树套splay)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了刷题总结——二逼平衡树(bzoj3224线段树套splay)相关的知识,希望对你有一定的参考价值。

题目:

Description

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)

Input

第一行两个数 n,m 表示长度为n的有序序列和m个操作
第二行有n个数,表示有序序列
下面有m行,opt表示操作标号
若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名
若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数
若opt=3 则为操作3,之后有两个数pos,k 表示将pos位置的数修改为k
若opt=4 则为操作4,之后有三个数l,r,k 表示查询区间[l,r]内k的前驱
若opt=5 则为操作5,之后有三个数l,r,k 表示查询区间[l,r]内k的后继

Output

对于操作1,2,4,5各输出一行,表示查询结果

Sample Input

9 6
4 2 2 1 9 4 0 1 1
2 1 4 3
3 4 10
2 1 4 3
1 2 5 9
4 3 9 5
5 2 8 5

Sample Output

2
4
3
4
9

HINT

1.n和m的数据范围:n,m<=50000

2.序列中每个数的数据范围:[0,1e8]

3.虽然原题没有,但事实上5操作的k可能为负数

题解:

  哎···这道题充分证明了我的代码能力和专注力是tm有多弱····

  调了一个早上的代码···终于发现错哪里了····只是因为update里面的=写成了+=···

  第一我打的时候打错了···第二我调的时候竟然没有看到这一点···我勒个大艹···

  然后一个上午的时间就荒废在了这一个等号里···

  下次打的时候我tm一定要注意细节了···不要以为打得顺手就打得正确···

  题解的话···我在找标称对拍的时候惊讶的发现网上的一个小姐姐竟然和我写得几乎一模一样·····这里就引用她的吧(其实我splay的版基本都是看她的···)%%%%%%Clove_unique

  线段树套splay,简单地说就是线段树的每一个节点都吊着一颗splay,表示的是线段树当前节点所表示的区间的点,按权值排序。 
  Q1:线段树常规查询区间,每一次统计小于k的点的个数再相加。 
  Q2:这个是最麻烦也是最精妙的一问,解决方法是二分答案,每二分到一个答案查询一下这个答案在这个区间内的排名,如果排名等于k+1的话返回它的pre即可。注意这里二分满   足条件之后不用查询pre,答案直接为head-1,可以证明head-1一定在序列中。 
  Q3:相当于线段树的点修改,在splay中删除再插入即可。 
  Q4:线段树常规查询区间,每一次找区间内比k小的最大的数,然后取max 
  Q5:类似于Q4,每一次找区间内比k大的最小的数,然后取min 

  自己再解释一下Q2的操作吧···询问的是排名为k的数···我们先找出第一个大于至少k的数最小的数x··如果是等于,那么此时left-1肯定是答案(最后right会等于left),如果是大于多于   k个数量的数··那么  想当于是x大于x-1,而x-1又大于少于k个数量的数···这种情况下x-1肯定是有重复的个数的,在连续的x-1中的某一x-1肯定刚好大于等于k个数量的数···那么x-1,即   left-1就是答案····如果x是大于等于k个数量的数,显然x-1是答案

  当然这道题套treap会快太多···然而我并不想转treap···指针写起来太麻烦····

  另外这道题如果怕爆空间可以回收节点·····然而我懒得写了···

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=4e6+5;
int root[N],son[N][2],father[N],key[N],size[N],tot,n,m,num[50005],maxx=0,cnt[N];
const int inf=1e9;
inline int R()
{
  char c;int f=0,i=1;
  for(c=getchar();(c<0||c>9)&&c!=-;c=getchar());
  if(c==-)  c=getchar(),i=-1;
  for(;c<=9&&c>=0;c=getchar())
    f=(f<<3)+(f<<1)+c-0; 
  return f*i;
}
inline void clear(int now)
{
  if(!now)  return;
  cnt[now]=son[now][0]=son[now][1]=father[now]=size[now]=0;key[N]=0;
}
inline void update(int now)
{
  if(!now)  return;
  size[now]=cnt[now]+(son[now][0]?size[son[now][0]]:0)+(son[now][1]?size[son[now][1]]:0);  
}
inline int get(int now){return son[father[now]][1]==now;}
inline void rotate(int now)
{
  int fa=father[now],ofa=father[fa],which=get(now);
  son[fa][which]=son[now][which^1],father[son[fa][which]]=fa;
  son[now][which^1]=fa,father[fa]=now,father[now]=ofa;
  if(ofa)  son[ofa][son[ofa][1]==fa]=now;
  update(fa),update(now);
}
inline void splay(int k,int now)
{
  while(father[now])
  {
    if(father[father[now]])
      rotate(get(now)==get(father[now])?father[now]:now);
    rotate(now);
  }
  root[k]=now;
}
inline int findkth(int k,int v)  //查询排名 
{
  int now=root[k],ans=0;
  while(true)
  {  
    if(!now)  return ans;
    if(v==key[now])  return (son[now][0]?size[son[now][0]]:0)+ans;
    else if(v>key[now])
    {  
      ans+=(son[now][0]?size[son[now][0]]:0)+cnt[now];
      now=son[now][1];
    }
    else if(v<key[now])  now=son[now][0];
  }
}
inline int findpos(int k,int v)  //找到位置 
{
  int now=root[k];
  while(true)
  {
    if(v==key[now])  return now;
    else if(v<key[now])  now=son[now][0];
    else now=son[now][1];
  }
}
inline void insert(int k,int v)
{
  int now=root[k],last=0;
  while(true)
  {
    if(!now)
    {
      now=++tot;father[now]=last;key[now]=v;size[now]=cnt[now]=1;son[now][1]=son[now][0]=0;
      if(!root[k])  root[k]=now;
      else
      {
        son[last][v>key[last]]=now;
        update(last);
        splay(k,now);
      }
      break;
    }
    else if(v==key[now])
    {
      cnt[now]++;update(now);update(last);
      splay(k,now);
      break;
    }
    last=now;now=son[now][v>key[now]];
  }
}
inline int pre(int k)
{ 
  int now=son[root[k]][0];
  while(son[now][1])  now=son[now][1]; 
  return now;
}
inline void del(int k,int v)
{
  int now=findpos(k,v);
  splay(k,now);
  if(cnt[root[k]]>1)  {cnt[root[k]]--,update(root[k]);return;}  
  else if(!son[root[k]][0]&&!son[root[k]][1]){clear(root[k]);root[k]=0;return;}
  else if(!son[root[k]][0])
  {
    int oldroot=root[k];root[k]=son[root[k]][1];father[root[k]]=0;
    clear(oldroot);return;
  }
  else if(!son[root[k]][1])
  {
    int oldroot=root[k];root[k]=son[root[k]][0];father[root[k]]=0;
    clear(oldroot);return;
  }
  else 
  {
    int oldroot=root[k];
    int leftbig=pre(k);splay(k,leftbig);
    son[root[k]][1]=son[oldroot][1];
    father[son[root[k]][1]]=root[k];
    update(root[k]);clear(oldroot);return;
  }
}
inline int findpre(int k,int v)
{  
  int now=root[k],ans=0;
  while(now)
  {
    if(key[now]<v)
    {
      if(ans<key[now]) ans=key[now];
      now=son[now][1];
    }
    else now=son[now][0];
  }
  return ans;
}
inline int findnxt(int k,int v)
{
  int now=root[k],ans=inf;
  while(now)
  {
    if(key[now]>v)
    {
      if(ans>key[now])  ans=key[now];
      now=son[now][0];
    }
    else now=son[now][1];
  }
  return ans;
}
//---------------------------------------------splay
inline void seginsert(int k,int l,int r,int x,int v)
{ 
  insert(k,v);
  if(l==r)  return;
  int mid=(l+r)/2;
  if(x<=mid)  seginsert(k*2,l,mid,x,v);
  else seginsert(k*2+1,mid+1,r,x,v);
  return;
}
inline int segfindkth(int k,int l,int r,int x,int y,int v)
{
  if(l>=x&&r<=y)  return findkth(k,v);
  int mid=(l+r)/2;int temp=0;
  if(x<=mid)  temp+=segfindkth(k*2,l,mid,x,y,v);
  if(y>mid)  temp+=segfindkth(k*2+1,mid+1,r,x,y,v);
  return temp;
}
inline void segmodify(int k,int l,int r,int x,int v)
{
  del(k,num[x]);
  insert(k,v);
  if(l==r)  return;
  int mid=(l+r)/2;
  if(x<=mid)  segmodify(k*2,l,mid,x,v);
  else segmodify(k*2+1,mid+1,r,x,v);
} 
inline int segpre(int k,int l,int r,int x,int y,int v)
{
  if(l>=x&&r<=y)  return findpre(k,v);
  int mid=(l+r)/2;int temp=0;
  if(x<=mid)  temp=max(temp,segpre(k*2,l,mid,x,y,v)); 
  if(y>mid)  temp=max(temp,segpre(k*2+1,mid+1,r,x,y,v));
  return temp;
}
inline int segnxt(int k,int l,int r,int x,int y,int v)
{
  if(l>=x&&r<=y)  return findnxt(k,v);
  int mid=(l+r)/2;int temp=inf;
  if(x<=mid)  temp=min(temp,segnxt(k*2,l,mid,x,y,v));
  if(y>mid)  temp=min(temp,segnxt(k*2+1,mid+1,r,x,y,v));
  return temp;
}
inline void dfs(int now)
{
  cout<<key[now]<<" ";
  if(son[now][0])  dfs(son[now][0]);
  if(son[now][1])  dfs(son[now][1]);
}
int main()
{
  n=R(),m=R();int op,a,b,c;
  for(int i=1;i<=n;i++)  a=R(),seginsert(1,1,n,i,a),num[i]=a,maxx=max(maxx,num[i]);
  while(m--)
  {
    op=R(); 
    if(op==1) {a=R(),b=R(),c=R();printf("%d\n",segfindkth(1,1,n,a,b,c)+1);}
    else if(op==2)  
    {
      a=R(),b=R(),c=R();
      int le=0,ri=maxx+1;int ans=0;
      while(le!=ri)
      {
        int mid=(le+ri)/2;
        int temp=segfindkth(1,1,n,a,b,mid); 
        if(temp<c)  le=mid+1;
        else ri=mid;
      }
      printf("%d\n",le-1);
    }
    else if(op==3)  {a=R(),b=R();segmodify(1,1,n,a,b);num[a]=b;maxx=max(maxx,b);}
    else if(op==4)  {a=R(),b=R(),c=R();printf("%d\n",segpre(1,1,n,a,b,c));}
    else if(op==5)  {a=R(),b=R(),c=R();printf("%d\n",segnxt(1,1,n,a,b,c));} 
  }
  return 0;
}

 





























以上是关于刷题总结——二逼平衡树(bzoj3224线段树套splay)的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 3196/ Tyvj 1730 二逼平衡树 (线段树套平衡树)

bzoj3196 [TYVJ1730]二逼平衡树 树套树 线段树套ScapeGoat_Tree

bzoj 3196 && luogu 3380 JoyOI 1730 二逼平衡树 (线段树套Treap)

BZOJ3196 二逼平衡树 线段树套平衡树

[bzoj3196][Tyvj 1730][二逼平衡树] (线段树套treap)

bzoj3196 Tyvj 1730 二逼平衡树 线段树套Treap