【bzoj3196】二逼平衡树【树套树】【线段树】【平衡树】【呵呵】

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-1 23:23   34   0

……

我承认我写change函数时确实213了- -

a[pos]应当在最后被修改,可我却忘了……

第一次交上去的时候爆了数组,WA了……

然后把数组开大,交上去,MLE了……

现在我证明长度为n的序列,他的平衡树节点数组只要开到(log(2,f(n))+1)*f(n)就绝对不会爆。

f(n)是指比n大的最小的2的幂,比如f(65535)=65536,f(65537)=2^17.

证明嘛,因为长16的线段树有5层,每层平衡树总数都是16……

下面放代码,请神犇轻喷- -

欢迎评论。

#include<cstdio>
#include<cstring>
#include<iostream>
#define mid (l+r>>1)
using namespace std;
const int maxn=1200000;
typedef int arr[maxn];
int tot,m,n;
arr l,r,s,d;
int a[50001];
struct SBT{
 int rt;
 SBT():rt(0){}
 void L(int &x){
  int b=r[x];
  r[x]=l[b];
  l[b]=x;
  s[b]=s[x];
  s[x]=s[l[x]]+s[r[x]]+1;
  x=b;
 }
 void R(int &x){
  int b=l[x];
  l[x]=r[b];
  r[b]=x;
  s[b]=s[x];
  s[x]=s[l[x]]+s[r[x]]+1;
  x=b;
 }
 void maintain(int &x,bool f){
  if(!f){
   if(s[l[l[x]]]>s[r[x]])
    R(x);
   else if(s[r[l[x]]]>s[r[x]])
    L(l[x]),R(x);
   else return;
  }
  else{
   if(s[r[r[x]]]>s[l[x]])
    L(x);
   else if(s[l[r[x]]]>s[l[x]])
    R(r[x]),L(x);
   else return;
  }
  maintain(l[x],0);
  maintain(r[x],1);
  maintain(x,0);
  maintain(x,1);
 }
 void insert(int &x,int key){
  if(!x){
   x=++tot;
   s[x]=1;
   l[x]=r[x]=0;
   d[x]=key;
  }
  else{
   s[x]++;
   insert(key<d[x]?l[x]:r[x],key);
   maintain(x,key>=d[x]);
  }
 }
 int del(int &x,int key){
  s[x]--;
  if(key==d[x]||key<d[x]&&!l[x]||key>d[x]&&!r[x]){
   int val=d[x];
   if(!l[x]||!r[x])
    x=l[x]+r[x];
   else d[x]=del(l[x],key+1);
   return val;
  }
  return del(key<d[x]?l[x]:r[x],key);
 }
 int rank(int t,int key){
  int ans=0;//因为要把多个区间的结果并起来,所以只返回比key小的数的个数。 
  while(t){
   if(key<=d[t]) t=l[t];
   else ans+=s[l[t]]+1,t=r[t];
  }
  return ans;
 }
 int succ(int t,int key){
  int f=0;
  while(t){
   if(key<d[t]) f=t,t=l[t];
   else t=r[t];
  }
  return f?d[f]:0x1f1f1f1f;
 }
 int pred(int t,int key){
  int f=0;
  while(t){
   if(d[t]<key) f=t,t=r[t];
   else t=l[t];
  }
  return f?d[f]:-1;
 }
 void insert(int key){insert(rt,key);  }
 void del   (int key){del(rt,key);  }
 int  select(int k  ){return select(rt,k);}
 int  rank  (int k  ){return rank(rt,k);  }
 int  succ  (int key){return succ(rt,key);}
 int  pred  (int key){return pred(rt,key);}
};
//----------------------------------------------
//segment tree:
arr lc,rc;
SBT sbt[100000];
int stot=0,tree=0;
void build(int &x,int l,int r){
 x=++stot;
 for(int i=l;i<r;++i)//!!!!!!!!!!!!!!!!!!!!
  sbt[x].insert(a[i]);
 if(l+1<r){
  build(lc[x],l,mid);
  build(rc[x],mid,r);
 }
}
void change(int x,int l,int r,int pos,int c){
 if(l+1==r){
  sbt[x].del(a[pos]);
  sbt[x].insert(c);
  a[pos]=c;
  return;
 }
 sbt[x].del(a[pos]);
 sbt[x].insert(c);
 if(pos<mid)
  change(lc[x],l,mid,pos,c);
 else
  change(rc[x],mid,r,pos,c);

}
int rank(int x,int l,int r,int L,int R,int key){
 if(L<=l&&r<=R)
  return sbt[x].rank(key);
 int ans=0;
 if(L<mid) ans+=rank(lc[x],l,mid,L,R,key);
 if(R>mid) ans+=rank(rc[x],mid,r,L,R,key);
 return ans;
}
int succ(int x,int l,int r,int L,int R,int key){
 if(L<=l&&r<=R) return sbt[x].succ(key);
 int ans=0x1f1f1f1f;
 if(L<mid) ans=min(ans,succ(lc[x],l,mid,L,R,key));
 if(R>mid) ans=min(ans,succ(rc[x],mid,r,L,R,key));
 return ans;
}
int pred(int x,int l,int r,int L,int R,int key){
 if(L<=l&&r<=R) return sbt[x].pred(key);
 int ans=-1;
 if(L<mid) ans=max(ans,pred(lc[x],l,mid,L,R,key));
 if(R>mid) ans=max(ans,pred(rc[x],mid,r,L,R,key));
 return ans;
}
#undef mid
inline int Select(int L,int R,int k){
 int low=0,high=(int)1e8,tmpRank,mid;
 while(low<=high){
  mid=low+high>>1;
  tmpRank=1+rank(1,1,n+1,L,R,mid);
  if(tmpRank>k) high=mid-1;
  else low=mid+1;
 }
 return high;
}
inline int Succ(int L,int R,int key){
 return succ(1,1,n+1,L,R,key);
}
inline int Pred(int L,int R,int key){
 return pred(1,1,n+1,L,R,key);
}
inline int Rank(int L,int R,int key){
 return 1+rank(1,1,n+1,L,R,key);
}
inline void read(int &x){
    x=0;
    bool f=false;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!=-1){if(ch=='-') f=true;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
    if(f) x=-x;
}
int main(){
 read(n),read(m);
 for(int i=1;i<=n;++i)
  read(a[i]);
 build(tree,1,n+1);
 int a,b,c,opt;
 while(m--){
  read(opt);
  switch(opt){
   case 1://求排名
    read(a);read(b);read(c);//scanf("%d%d%d",&a,&b,&c);
    printf("%d\n",Rank(a,b+1,c));
    break;
   case 2://求区间第k大
    read(a);read(b);read(c);//scanf("%d%d%d",&a,&b,&c);
    printf("%d\n",Select(a,b+1,c));
    break;
   case 3://修改元素的值
    read(a);read(b);//scanf("%d%d",&a,&b);
    change(1,1,n+1,a,b);
    break;
   case 4://求区间前驱
    read(a);read(b);read(c);//scanf("%d%d%d",&a,&b,&c);
    printf("%d\n",Pred(a,b+1,c));
    break;
   case 5://求区间后继
    read(a);read(b);read(c);//scanf("%d%d%d",&a,&b,&c);
    printf("%d\n",Succ(a,b+1,c));
    
  }
 }
 return 0;
}

PS:区间后继的求法(区间前驱同理):当这个区间被线段树分成好几个区间之后,在这几个区间里分别求后继,然后取最小值。注意如果在某个区间里没有后继,则返回正无穷,以免影响答案。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP