您的位置:首页 > 其它

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

2015-02-17 16:31 357 查看
……

我承认我写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:区间后继的求法(区间前驱同理):当这个区间被线段树分成好几个区间之后,在这几个区间里分别求后继,然后取最小值。注意如果在某个区间里没有后继,则返回正无穷,以免影响答案。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: