1. 程式人生 > >刷題總結——二逼平衡樹(bzoj3224線段樹套splay)

刷題總結——二逼平衡樹(bzoj3224線段樹套splay)

++ output 數列 turn 表示 roo string 修改 和我

題目:

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)