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

来源:互联网 发布:卡巴斯基软件下载 编辑:程序博客网 时间:2024/06/06 20:36

……

我承认我写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);elsechange(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 midinline 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 0