平衡树三题:bzoj3224+bzoj3223+bzoj3196 普通平衡树 文艺平衡树 二逼平衡树(splay)

来源:互联网 发布:淘宝开放平台难吗 编辑:程序博客网 时间:2024/05/18 02:03

bzoj3224 Tyvj 1728 普通平衡树

原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3224

题意:
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

数据范围
1.n的数据范围:n<=100000

2.每个数的数据范围:[-2e9,2e9]

题解:
基础平衡树操作。
旧代码。

代码:

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;const int N=100005;int n;int root=0;int fa[N],ch[N][2],size[N],cnt[N],key[N];int sz=0;inline void update(int k){    if(k)    {           size[k]=cnt[k];        if(ch[k][0]) size[k]+=size[ch[k][0]];        if(ch[k][1]) size[k]+=size[ch[k][1]];    }    return;}inline bool get(int x){return ch[fa[x]][1]==x;}inline void rotate(int x,int &k){    int y=fa[x],z=fa[y];    int l,r;    if(ch[y][0]==x) l=0;else l=1;r=l^1;    if(y==k) k=x;    else    {        if(ch[z][0]==y) ch[z][0]=x; else ch[z][1]=x;    }    ch[y][l]=ch[x][r]; fa[ch[x][r]]=y;    ch[x][r]=y; fa[x]=z; fa[y]=x;    update(y);update(x);    return;}inline void splay(int x,int &k){    while(x!=k)    {        int y=fa[x],z=fa[y];        if(y!=k)        {            if(x==ch[y][0]^y==ch[z][0]) rotate(x,k);            else rotate(y,k);        }        rotate(x,k);    }    return;}inline int findx(int x){    int now=root;    while(1)    {        if(ch[now][0]&&x<=size[ch[now][0]]) now=ch[now][0];        else        {            int tmp=cnt[now]+(ch[now][0]?size[ch[now][0]]:0);            if(x<=tmp) return key[now];            x-=tmp; now=ch[now][1];         }    }}inline void insert(int x){    if(root==0) { sz++;size[sz]=1;ch[sz][0]=ch[sz][1]=0;root=sz;cnt[sz]=1;fa[sz]=0;key[sz]=x; return; }    int now=root,f=0;    while(1)    {        if(key[now]==x)        {size[now]++;cnt[now]++;update(f);splay(now,root); break;}         if(x<key[now]) { f=now;now=ch[now][0];}        else{ f=now; now=ch[now][1];}        if(now==0)        {            sz++;now=sz;size[sz]=1;cnt[sz]=1;ch[sz][0]=ch[sz][1]=0;fa[sz]=f;key[sz]=x;            if(x<key[f]) ch[f][0]=sz; else ch[f][1]=sz;            update(f); splay(now,root);break;        }      }    return;}inline int find(int x){    int now=root,ans=0;    while(1)    {        if(x<key[now]) now=ch[now][0];        else        {            ans+=(ch[now][0]?size[ch[now][0]]:0);            if(key[now]==x){ splay(now,root); return ans+1;}            ans+=cnt[now]; now=ch[now][1];        }             }}inline void clear(int k){    ch[k][0]=ch[k][1]=key[k]=cnt[k]=size[k]=fa[k]=0;return;}inline int getpre(){    int now=ch[root][0];    while(ch[now][1]) now=ch[now][1];    return now;}inline int getnxt(){    int now=ch[root][1];    while(ch[now][0]) now=ch[now][0];    return now;}inline void de(int x){    int now=root,f=0;    if(root==0) return;    int sss=find(x);    if(cnt[root]>1){cnt[root]--; update(root);return;}         if(!ch[root][0]&&!ch[root][1]) {clear(root); root=0;return;} //注意改根清空。    if(!ch[root][0]) {int oldroot=root;root=ch[oldroot][1]; fa[root]=0; clear(oldroot);return;}    if(!ch[root][1]) {int oldroot=root;root=ch[oldroot][0]; fa[root]=0; clear(oldroot);return;}    else    {        int pre=getpre();        int oldroot=root;        splay(pre,root);        fa[ch[oldroot][1]]=root; ch[root][1]=ch[oldroot][1];        clear(oldroot);update(root);        return;    }          }int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)    {        int a,b;        scanf("%d%d",&a,&b);        switch(a){            case 1: insert(b); break;            case 2: de(b); break;            case 3: printf("%d\n",find(b)); break;            case 4: printf("%d\n",findx(b)); break;            case 5: {                insert(b);                printf("%d\n",key[getpre()]);                de(b);                break;            }            case 6: {                insert(b);                printf("%d\n",key[getnxt()]);                de(b);                break;            }        }    }    return 0;}

bzoj3223 Tyvj 1729 文艺平衡树

原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3223

题意:
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1

数据范围
N,M<=100000

题解:
基础splay操作。
旧代码。

代码:

#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int N=100005;int fa[N],ch[N][2],id[N],size[N];int n,m,root;bool rev[N];void update(int k){    size[k]=size[ch[k][0]]+size[ch[k][1]]+1;}void build(int l,int r,int f){    if(l>r) return;    int now=id[l];int last=id[f];    if(l==r)    {        size[now]=1;rev[now]=0;fa[now]=last;        if(l<f) ch[last][0]=now;        else ch[last][1]=now;        return;    }    else    {        int mid=(l+r)>>1;        now=id[mid];        build(l,mid-1,mid);build(mid+1,r,mid);        rev[now]=0; fa[now]=last;        if(mid<f) ch[last][0]=now;        else ch[last][1]=now;        update(now);    }}void pushdown(int k){        if(rev[k])    {        swap(ch[k][0],ch[k][1]);        rev[ch[k][0]]^=1;        rev[ch[k][1]]^=1;        rev[k]=0;    }}void rotate(int x,int &k){    int y=fa[x]; int z=fa[y];    int l,r;    if(ch[y][0]==x) l=0; else l=1; r=l^1;    if(y==k) k=x;    else    {if(ch[z][0]==y) ch[z][0]=x; else ch[z][1]=x;}    fa[x]=z; fa[y]=x; fa[ch[x][r]]=y;     ch[y][l]=ch[x][r];ch[x][r]=y;    update(y);update(x);}void splay(int x,int &k) //传地址的意义 在于直接改变root {         while(x!=k)    {        int y=fa[x],z=fa[y];        if(y!=k)        {            if(ch[y][0]==x^ch[z][0]==y)            rotate(x,k);            else rotate(y,k);        }                rotate(x,k);    }}int find(int k,int rank){    pushdown(k);    int l=ch[k][0];int r=ch[k][1];    if(size[l]+1==rank) return k;    if(size[l]>=rank) return find(l,rank);    else    return find(r,rank-size[l]-1);    }void rever(int l,int r){    int x=find(root,l);int y=find(root,r+2);    splay(x,root);    splay(y,ch[x][1]);      int z=ch[y][0];    rev[z]^=1;}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n+2;i++)    id[i]=i;    root=(n+3)>>1;        build(1,n+2,0);    int l,r;    for(int i=1;i<=m;i++)    {               scanf("%d%d",&l,&r);        rever(l,r);    }    for(int i=1;i<=n;i++) printf("%d ",find(root,i+1)-1);    return 0;}

bzoj3196 Tyvj 1730 二逼平衡树

原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3196

题意:
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)

数据范围
1.n和m的数据范围:n,m<=50000
2.序列中每个数的数据范围:[0,1e8]
3.虽然原题没有,但事实上5操作的k可能为负数

题解:
线段树套splay(大常数组合orz)
线段树的每一个节点建一个该区间的平衡树。(空间O(nlogn))
opt1:查询覆盖该区间的线段树节点对应平衡树中比k小的个数+1
opt2:二分这个数,转化为opt1
opt3:该点向上一条链的线段树平衡树中都删除一个这个点对应的值,插入一个这个点的新值。
opt4:查询覆盖该区间的线段树节点对应平衡树中该值前驱并取最大。
opt5:查询覆盖该区间的线段树节点对应平衡树中该值后并取最小。
(200行+的代码_ (:P」∠)_ )

代码:

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;const int N=50005;const int NN=2000005;const int inf=0x3f3f3f3f;struct node{    int ch[2],fa,val,size,cnt;    void init()    {        ch[0]=ch[1]=fa=val=size=cnt=0;    }}tr[NN];struct NODE{    int ls,rs,root;    void init()    {        ls=rs=root=0;    }}TR[2*N];int n,m,tail=0,TAIL=0,a[N],root;void update(int nd){    int ls=tr[nd].ch[0]; int rs=tr[nd].ch[1];    tr[nd].size=tr[nd].cnt;    if(ls) tr[nd].size+=tr[ls].size;    if(rs) tr[nd].size+=tr[rs].size;}int insert(int &nd,int fa,int val){    if(!nd) {nd=++tail; tr[nd].init(); tr[nd].fa=fa; tr[nd].size=tr[nd].cnt=1; tr[nd].val=val; return nd;}    int pos;    if(val<tr[nd].val) pos=insert(tr[nd].ch[0],nd,val);    else if(val>tr[nd].val) pos=insert(tr[nd].ch[1],nd,val);    else {tr[nd].cnt++; pos=nd;}    update(nd);     return nd;}void rotate(int x,int &top){    int y=tr[x].fa; int z=tr[y].fa;    if(y==top) top=x;    else  if(tr[z].ch[0]==y) tr[z].ch[0]=x; else tr[z].ch[1]=x;    tr[x].fa=z;    int l= (tr[y].ch[0]==x)? 0:1; int r=l^1;    tr[y].ch[l]=tr[x].ch[r]; tr[tr[x].ch[r]].fa=y;    tr[x].ch[r]=y; tr[y].fa=x;    update(y); update(x);}void splay(int x,int &top){    while(x!=top)    {        int y=tr[x].fa; int z=tr[y].fa;        if(y!=top)        {            if(tr[y].ch[0]==x ^ tr[z].ch[0]==y) rotate(x,top);            else rotate(y,top);        }        rotate(x,top);    }}void build(int &nd,int lf,int rg){    nd=++TAIL;    for(int i=lf;i<=rg;i++) {int pos=insert(TR[nd].root,0,a[i]); splay(pos,TR[nd].root);}     if(lf==rg) return;    int mid=(lf+rg)>>1;    build(TR[nd].ls,lf,mid);    build(TR[nd].rs,mid+1,rg);}int query(int nd,int val){    if(!nd) return 0;    if(tr[nd].val==val) return tr[tr[nd].ch[0]].size;    else if(tr[nd].val<val) return tr[tr[nd].ch[0]].size+tr[nd].cnt+query(tr[nd].ch[1],val);    else return query(tr[nd].ch[0],val);}int query(int nd,int lf,int rg,int L,int R,int val){    if(L<=lf&&rg<=R) return query(TR[nd].root,val);    int ans=0;    int mid=(lf+rg)>>1;    if(L<=mid) ans+=query(TR[nd].ls,lf,mid,L,R,val);    if(R>mid) ans+=query(TR[nd].rs,mid+1,rg,L,R,val);    return ans;}int find(int nd,int val){    if(!nd) return 0;    int ls=tr[nd].ch[0]; int rs=tr[nd].ch[1];    if(tr[nd].val==val) return nd;    else if(val<tr[nd].val) return find(tr[nd].ch[0],val);    else return find(tr[nd].ch[1],val);}int getpre(int x){    int tmp=tr[x].ch[0];    while(tr[tmp].ch[1]) tmp=tr[tmp].ch[1];    return tmp;}void del(int &x){    if(tr[x].cnt>1) {tr[x].cnt--;  update(x); return;}    if(!tr[x].ch[0]&&!tr[x].ch[1]) { tr[x].init(); x=0; return;}        else if(!tr[x].ch[0]||!tr[x].ch[1])    {        int l= tr[x].ch[0]==0? tr[x].ch[1]:tr[x].ch[0];        tr[x].init(); x=l; tr[x].fa=0; update(x); return;    }    int now=x; int pre=getpre(now);    splay(pre,x);    tr[pre].ch[1]=tr[now].ch[1];    tr[tr[now].ch[1]].fa=pre;    tr[now].init();    update(pre);    return;}void modify(int &nd,int pos,int val){    int pv=a[pos];    int x=find(nd,pv);    splay(x,nd); del(nd);    int now=insert(nd,n,val); splay(now,nd);}void modify(int nd,int lf,int rg,int pos,int val){    modify(TR[nd].root,pos,val);    if(lf==rg) return;    int mid=(lf+rg)>>1;    if(pos<=mid) modify(TR[nd].ls,lf,mid,pos,val);    if(pos>mid) modify(TR[nd].rs,mid+1,rg,pos,val);}int preval(int nd,int val){    int tmp=nd; int ans=-inf;    while(tmp)    {        if(tr[tmp].val<val)         {            ans=max(tr[tmp].val,ans);            tmp=tr[tmp].ch[1];        }        else tmp=tr[tmp].ch[0];    }    return ans;}int nxtval(int nd,int val){    int tmp=nd; int ans=inf;    while(tmp)    {        if(tr[tmp].val>val)         {            ans=min(tr[tmp].val,ans);            tmp=tr[tmp].ch[0];        }        else tmp=tr[tmp].ch[1];    }    return ans;}int query_pre(int nd,int lf,int rg,int L,int R,int val){    if(L<=lf&&rg<=R) return preval(TR[nd].root,val);    int ans=-inf;    int mid=(lf+rg)>>1;    if(L<=mid) ans=max(ans,query_pre(TR[nd].ls,lf,mid,L,R,val));    if(R>mid) ans=max(ans,query_pre(TR[nd].rs,mid+1,rg,L,R,val));    return ans;}int query_nxt(int nd,int lf,int rg,int L,int R,int val){    if(L<=lf&&rg<=R) return nxtval(TR[nd].root,val);    int ans=inf;    int mid=(lf+rg)>>1;    if(L<=mid) ans=min(ans,query_nxt(TR[nd].ls,lf,mid,L,R,val));    if(R>mid) ans=min(ans,query_nxt(TR[nd].rs,mid+1,rg,L,R,val));    return ans;}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++) scanf("%d",&a[i]);      tr[0].init(); build(root,1,n);    while(m--)    {        int opt,l,r,pos,k; scanf("%d",&opt);        if(opt==1) //查询k在区间[l,r]内的排名        {            scanf("%d%d%d",&l,&r,&k);            printf("%d\n",query(root,1,n,l,r,k)+1);        }        else if(opt==2)//之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数        {            scanf("%d%d%d",&l,&r,&k);            int lf=0; int rg=100000000;            while(lf+1<rg)            {                int mid=(lf+rg)>>1;                if(query(root,1,n,l,r,mid)+1<=k) lf=mid;                else rg=mid;            }            if(query(root,1,n,l,r,rg)+1<=k) printf("%d\n",rg);            else printf("%d\n",lf);        }        else if(opt==3)//之后有两个数pos,k 表示将pos位置的数修改为k        {            scanf("%d%d",&pos,&k);            modify(root,1,n,pos,k);            a[pos]=k;        }        else if(opt==4)        {            scanf("%d%d%d",&l,&r,&k); //之后有三个数l,r,k 表示查询区间[l,r]内k的前驱            printf("%d\n",query_pre(root,1,n,l,r,k));        }        else if(opt==5)        {            scanf("%d%d%d",&l,&r,&k); //之后有三个数l,r,k 表示查询区间[l,r]内k的后驱            printf("%d\n",query_nxt(root,1,n,l,r,k));        }    }       return 0;}
原创粉丝点击