刷题总结——二逼平衡树(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;
}
时间: 2024-08-02 19:05:50

刷题总结——二逼平衡树(bzoj3224线段树套splay)的相关文章

3196. 二逼平衡树【线段树套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在区间

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

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,

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

题目 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱定义为小于x,且最大的数) 5.查询k在区间内的后继(后继定义为大于x,且最小的数) 输入格式 第一行两个数 n,m 表示长度为n的有序序列和m个操作 第二行有n个数,表示有序序列 下面有m行,opt表示操作标号 若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名 若

BZOJ3196 二逼平衡树 ZKW线段树套vector(滑稽)

我实在是不想再打一遍树状数组套替罪羊树了... 然后在普通平衡树瞎逛的时候找到了以前看过vector题解 于是我想:为啥不把平衡树换成vector呢??? 然后我又去学了一下ZKW线段树 就用ZKW线段树套vector水过啦!!! 每个ZKW线段树的节点保存一个vector 操作1在分出的vector上查询比它小的数有多少个然后相加再加1 操作2二分再上操作1 操作3修改需要修改的节点的vector 操作4在分出vector上查询前驱取最大 操作5与操作4同理 luogu主站5772ms上卡过,

[BZOJ 3196] 213平衡树 【线段树套set + 树状数组套线段树】

题目链接:BZOJ - 3196 题目分析 区间Kth和区间Rank用树状数组套线段树实现,区间前驱后继用线段树套set实现. 为了节省空间,需要离线,先离散化,这样需要的数组大小可以小一些,可以卡过128MB = = 嗯就是这样,代码长度= =我写了260行......Debug了n小时= = 代码 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #in

BZOJ3196 二逼平衡树

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,m 表示长度为n的有序序列和m个操作第二行有n个数

LibreOJ #106. 二逼平衡树

二次联通门 : LibreOJ #106. 二逼平衡树 /* LibreOJ #106. 二逼平衡树 写了整整一下午 1遍AC 感人肺腑啊... 我的做法比较中规中矩 线段树套splay 对于操作1没什么好说, 在线段树查询区间时查出排名 对于操作2, 一开始没想太多 在写完基础函数时突然意识到不好做 在想不出好办法后看了题解, 得知了要用二分 二分一下这个数, 二分出一个答案查排名 然后找前驱即可 操作3要在splay中删除再插入即可 操作4, 5没什么可说的...... 这个写法不太好..

【模板】二逼平衡树

线段树套splay: 这是个永远不想打第二遍的模板 1 #include<bits/stdc++.h> 2 #define R register 3 using namespace std; 4 const int N=50010,NN=2000010,oo=2147483647; 5 int n,m,a[N]; 6 int fa[NN],ch[NN][2],v[NN],siz[NN],cnt[NN],tot; 7 int ls[N<<2],rs[N<<2],root[

BZOJ 1901: Zju2112 Dynamic Rankings 区间k大 带修改 在线 线段树套平衡树

之前写线段树套splay数组版..写了6.2k..然后弃疗了.现在发现还是很水的..嘎嘎.. zju过不了,超时. upd:才发现zju是多组数据..TLE一版才发现.然后改了,MLE...手写内存池..尼玛终于过了..附zju2112代码于后. bzoj倒是过了,1A的感觉还是很爽的..可是时间不好看..这就是所谓\(O(nlog^3n)\)的复杂度的可怜之处么? 写挂的地方: insert一定要是传地址指针进去. delete时先把地址指针delete掉,最后把是地址指针指向左儿子or右儿子