非旋转treap之普通平衡树

 

普通平衡树
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
输入
第一行为n,表示操作的个数
下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
1.n的数据范围:n<=100000
2.每个数的数据范围:[-2e9,2e9]
输出
对于操作3,4,5,6每行输出一个数,表示对应答案
样例输入
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
样例输出
106465
84185
492737

 

Sol

1:加入元素
总结点个数tot增加,将权值赋好
然后在原树中查找其排名x(第多少小)
然后从原树中分裂出x-1个结点的树a
然后将a树与tot代表的树进行合并,再与从前树的剩余部分合并

2:删除权值为v的点
先找出排名x
然后从原树中分裂两个树,一个树有x个点
再从这个树中分裂出有x-1个点的树,再与原树的剩余部分合并

3:合并操作
对根结点编号为a,b的树进行合并。
此时要保证a树中所有点权值小于b树中所有点权值。
如果a,b中有一个为0,则以另一个为根
否则随机合并

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef pair<int,int> pii;
int tot,son[100005][2],c[100005],val[100005],root;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}
int random(int lim)
{
    return rand()%lim+1;
}
void updata(int a)
{
    c[a]=c[son[a][0]]+1+c[son[a][1]];
}
int getrank(int v)  //查找权值为v的数字的排名 
{
    int res=0;
    for(int x=root;x;)
    {
        if(val[x]<v)    
		//如果当前根结点小于v,则所有v及其左子树均小于v 
        {
            res+=c[son[x][0]]+1;
            x=son[x][1];
        }
        else
             x=son[x][0];
    }
    return res+1;
}
pii split(int a,int k)
//从a这个树中分出K个点来,
//这k个点所组成的树的树根做为其左返回值,其它的结点所形成的树做为右返回值
{
    if(c[a]==k)  //如果a这个树正好有k个结点,则直接返回a这个树,右返回值为0
	return make_pair(a,0);
    if(k==0)
	return make_pair(0,a);//左边为空树,右为原来a这个树 
    pii tmp;
    if(c[son[a][0]]>=k)//如果左子树结点个数大于等于k 
    {
        tmp=split(son[a][0],k);//从左子树中分离 
        son[a][0]=tmp.second;//a的左子树只剩下了分离后的结点 
        updata(a);//更新a的相关值 
        return make_pair(tmp.first,a);//返回分离后的树与从前a树剩下的部分 
    }
    else
    {
        tmp=split(son[a][1],k-c[son[a][0]]-1);//减去左子树及根结点
        son[a][1]=tmp.first;
        updata(a);
        return make_pair(a,tmp.second);
    }
}
int merge(int a,int b)
//将根结点编号分为a,b的树进行合并
//此时b树中所有点权值大于a树中的所有点权 
{
    if(!a||!b)return a+b;
    if(random(c[a]+c[b])<=c[a])
    {
        son[a][1]=merge(son[a][1],b);//将b做为a的右子树 
        updata(a);
        return a;
    }
    else
    {
        son[b][0]=merge(a,son[b][0]);//将a做为b的左子树 
        updata(b);
        return b;
    }
}
void insert(int v)
{
    tot++;
    val[tot]=v;
    memset(son[tot],0,sizeof(son[tot]));
    c[tot]=1;
    int x=getrank(v)-1;
    pii tmp=split(root,x);
	//从root中分离出前x个结点,tmp.first为分裂后左树的根结点编号 
	//tmp.second为分裂后右根的根结点编号 
    root=merge(merge(tmp.first,tot),tmp.second);
    //先将第tot个点与分离出来的左树合并,再与其右树合并 
    return;
}
void del(int v) //删除权值为v的数字 
{
    int x=getrank(v);
    pii tmp=split(root,x);
    int c=tmp.second;
    tmp=split(tmp.first,x-1);
    root=merge(tmp.first,c);
}
int getval(int a,int k)//查找第k小的数字 
{
    if(!k)return 0;
    if(c[son[a][0]]>=k)return getval(son[a][0],k);
    if(c[son[a][0]]+1==k)return a;
    return getval(son[a][1],k-c[son[a][0]]-1);
}
int main()
{
    srand(20020220);
    int n=read();
    for(int i=1;i<=n;i++)
    {
        int mode=read(),x=read();
        if(mode==1)insert(x);
        if(mode==2)del(x);
        if(mode==3)printf("%d\n",getrank(x));
        if(mode==4)printf("%d\n",val[getval(root,x)]);
        if(mode==5)printf("%d\n",val[getval(root,getrank(x)-1)]);
        if(mode==6)printf("%d\n",val[getval(root,getrank(x+1))]);
    }
    return 0;
}

  

序列终结者
网上有许多题,就是给定一个序列,要你支持几种操作:A、B、C、D。一看另一道题,又是一个序列 要支持几种
操作:D、C、B、A。尤其是我们这里的某人,出模拟试题,居然还出了一道这样的,真是没技术含量……这样 我
也出一道题,我出这一道的目的是为了让大家以后做这种题目有一个“库”可以依靠,没有什么其他的意思。这道
题目 就叫序列终结者吧。
【问题描述】
给定一个长度为N的序列,每个序列的元素是一个整数(废话)。
要支持以下三种操作:
1. 将[L,R]这个区间内的所有数加上V。
2. 将[L,R]这个区间翻转,比如1 2 3 4变成4 3 2 1。
3. 求[L,R]这个区间中的最大值。 最开始所有元素都是0。
输入
第一行两个整数N,M。M为操作个数。
以下M行,每行最多四个整数,依次为K,L,R,V。K表示是第几种操作,如果不是第1种操作则K后面只有两个数。
输出
对于每个第3种操作,给出正确的回答。

样例输入
4 4
1 1 3 2
1 2 4 -1
2 1 3
3 2 4

样例输出
2
提示
N<=50000,M<=100000。

//开始时数列值为空。。。。。。。。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef pair<int,int> pii;
int siz[50010],val[50010],son[50010][2],add[50010],maxx[50010],root,rev[50010];
int read()
{
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}
int random(int lim)
{
    return rand()*rand()%lim+1;
}
void pushdown(int a)
{
    if(rev[a])
    {
        rev[son[a][0]]^=1;
        rev[son[a][1]]^=1;
        rev[a]=0;
        swap(son[a][0],son[a][1]);
    }
    if(add[a])
    {
        val[a]+=add[a];
        add[son[a][0]]+=add[a];
        add[son[a][1]]+=add[a];
        maxx[son[a][0]]+=add[a];
        maxx[son[a][1]]+=add[a];
        add[a]=0;
    }
}
void updata(int a)
{
    siz[a]=1;
    maxx[a]=val[a];
    if(son[a][0])
    {
        siz[a]+=siz[son[a][0]];
        maxx[a]=max(maxx[a],maxx[son[a][0]]);
    }
    if(son[a][1])
    {
        siz[a]+=siz[son[a][1]];
        maxx[a]=max(maxx[a],maxx[son[a][1]]);
    }
}
int merge(int a,int b)
{
    if(!a||!b)return a+b;
    if(random(siz[a]+siz[b])<=siz[a])
    {
        pushdown(a);
        son[a][1]=merge(son[a][1],b);
        updata(a);
        return a;
    }
    else
    {
        pushdown(b);
        son[b][0]=merge(a,son[b][0]);
        updata(b);
        return b;
    }
}
pii split(int a,int b)
{
    if(!b)return 
	     make_pair(0,a);
    if(siz[a]==b)
	    return make_pair(a,0);
    pushdown(a);
    if(siz[son[a][0]]>=b)
    {
        pii tmp=split(son[a][0],b);
        son[a][0]=tmp.second;
        updata(a);
        return make_pair(tmp.first,a);
    }
    else
    {
        pii tmp=split(son[a][1],b-siz[son[a][0]]-1);
        son[a][1]=tmp.first;
        updata(a);
        return make_pair(a,tmp.second);
    }
}
void Add(int l,int r,int x)
{
    pii tmp;
    int a,b,c;
    tmp=split(root,r);
    //先分出有r个结点的树 
    c=tmp.second;
    tmp=split(tmp.first,l-1);
    //再分出有l-1个结点的树 
    a=tmp.first;
    b=tmp.second;
    //b所代表的树,即目标区域 
    add[b]+=x;maxx[b]+=x;
    root=merge(a,b);
    root=merge(root,c);
}
void fanzhuan(int l,int r)
{
    pii tmp=split(root,r);
    int a,b,c=tmp.second;
    tmp=split(tmp.first,l-1);
    a=tmp.first;
    b=tmp.second;
    rev[b]^=1;
    root=merge(a,b);
    root=merge(root,c);
}
void query(int l,int r)
{
    pii tmp=split(root,r);
    int a,b,c=tmp.second;
    tmp=split(tmp.first,l-1);
    a=tmp.first;
    b=tmp.second;
    printf("%d\n",maxx[b]);
    root=merge(a,b);
    root=merge(root,c);
}
int main()
{
    srand(3100605921);
    int n=read(),m=read();
    for(int i=1;i<=n;i++)
    siz[i]=1;
    root=1;
    for(int i=2;i<=n;i++)
    root=merge(root,i);
    while(m--)
    {
        int mode=read(),l=read(),r=read();
        if(mode==1)
        {
            int k=read();
            Add(l,r,k);
        }
        else
        if(mode==2)fanzhuan(l,r);
        else
        query(l,r);
    }
    return 0;
}

  

#include<bits/stdc++.h>
#define N 50005
using namespace std;
typedef pair<int,int> res;
inline int read(){
    int ans=0,w=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans*w;
}
int n,m,cnt=0,rt=0,val[N],son[N][2],siz[N],rev[N],rd[N],mx[N],add[N];
inline void pushup(int p){
    siz[p]=siz[son[p][0]]+siz[son[p][1]]+1;
    mx[p]=val[p];
    if(son[p][0])mx[p]=max(mx[p],mx[son[p][0]]);
    if(son[p][1])mx[p]=max(mx[p],mx[son[p][1]]);
}
inline int newnode()
{
    siz[++cnt]=1,son[cnt][0]=son[cnt][1]=0;
    val[cnt]=rev[cnt]=add[N]=mx[cnt]=0,rd[cnt]=rand();
    return cnt;
}
inline void pushadd(int p,int v){add[p]+=v,mx[p]+=v,val[p]+=v;}
inline void pushrev(int p){swap(son[p][0],son[p][1]),rev[p]^=1;}
inline void pushdown(int p){
    if(rev[p]){
        if(son[p][0])pushrev(son[p][0]);
        if(son[p][1])pushrev(son[p][1]);
        rev[p]=0;
    }
    if(add[p]){
        if(son[p][0])pushadd(son[p][0],add[p]);
        if(son[p][1])pushadd(son[p][1],add[p]);
        add[p]=0;
    }
}
inline int merge(int a,int b){
    if(!a||!b)return a+b;
    pushdown(a),pushdown(b);
    if(rd[a]>=rd[b]){son[a][1]=merge(son[a][1],b),pushup(a);return a;}
    son[b][0]=merge(a,son[b][0]),pushup(b);return b;
}
inline res split(int p,int k){
    if(!p)return res(0,0);
    res tmp,ans;
    pushdown(p);
    if(siz[son[p][0]]>=k){
        tmp=split(son[p][0],k);
        ans.first=tmp.first,son[p][0]=tmp.second,pushup(ans.second=p);
        return ans;
    }
    tmp=split(son[p][1],k-siz[son[p][0]]-1);
    ans.second=tmp.second,son[p][1]=tmp.first,pushup(ans.first=p);
    return ans;
}
inline int rank(int p,int v){
    if(!p)return 0;
    pushdown(p); 
    if(val[p]>v)return rank(son[p][0],v);
    return rank(son[p][1],v)+siz[son[p][0]]+1; 
}
inline void update(){
    int l=read(),r=read(),v=read();
    res x=split(rt,r),y=split(x.first,l-1);
    pushadd(y.second,v);
    rt=merge(merge(y.first,y.second),x.second);
}
inline void reverse(){
    int l=read(),r=read();
    res x=split(rt,r),y=split(x.first,l-1);
    pushrev(y.second);
    rt=merge(merge(y.first,y.second),x.second);
}
inline void query(){
    int l=read(),r=read();
    res x=split(rt,r),y=split(x.first,l-1);
    printf("%d\n",mx[y.second]);
    rt=merge(merge(y.first,y.second),x.second);
}
inline void ins(){
    int p=newnode();
    rt=merge(rt,p);
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i)ins();
    while(m--){
        int op=read();
        if(op==1)update();
        if(op==2)reverse();
        if(op==3)query();
    }
    return 0;
}

  

文艺平衡树
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如
原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
输入
第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数接下来m行每行两个数
[l,r] 数据保证 1<=l<=r<=n,N,M<=100000
输出
输出一行n个数字,表示原始序列经过m次变换后的结果
样例输入
5 3
1 3
1 3
1 4
样例输出
4 3 2 1 5

#include<cstdio>
#include<algorithm>
using namespace std;
typedef pair<int,int> pii;
int son[100010][2],rev[100010],siz[100010],root;
int read()
{
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}
int random(int lim)
{
    return rand()*rand()%lim+1;
}
void updata(int a)
{
    siz[a]=siz[son[a][0]]+siz[son[a][1]]+1;
}
void pushdown(int a)
{
    if(rev[a])  //如果a这个点上有标记则下传
    {
        rev[son[a][0]]^=1;//1变0,0变1
        rev[son[a][1]]^=1;
        swap(son[a][0],son[a][1]);
        rev[a]=0; //标记清空
    }
}
int merge(int a,int b)
{
    if(!a||!b)return a+b;
    if(random(siz[a]+siz[b])<=siz[a])
    {
        pushdown(a);
        son[a][1]=merge(son[a][1],b);
//将b与a的右子树合并,在此之前要进行翻转标记的下传,并交换左右子树
        updata(a);
        return a;
    }
    else
    {
        pushdown(b);
        son[b][0]=merge(a,son[b][0]);//将a与b的左子树合并
        updata(b);
        return b;
    }
}
pii split(int a,int k)
{
    if(!k)
        return make_pair(0,a);
    if(siz[a]==k)return 
      make_pair(a,0);
    pushdown(a);  //标记下传
    if(siz[son[a][0]]>=k)
    {
        pii tmp=split(son[a][0],k);
        son[a][0]=tmp.second;
        updata(a);
        return make_pair(tmp.first,a);
    }
    else
    {
        pii tmp=split(son[a][1],k-siz[son[a][0]]-1);
        son[a][1]=tmp.first;
        updata(a); 
        return make_pair(a,tmp.second);
    }
}
void fanzhuan(int l,int r)
{
    pii tmp=split(root,r);//将前r个点分出来
    int a,b,c=tmp.second;
    tmp=split(tmp.first,l-1);//前L-1个点分出来
    a=tmp.first;//a代表前l-1个点
    b=tmp.second;//b代表[L,R]个点
    rev[b]^=1;//所谓翻转,只是打上一个标记,并不翻转,等到需要访问时再翻转过来
    root=merge(a,b);
    root=merge(root,c);
}
void put(int a)
{
    pushdown(a);//a的标记下传
    if(!a)return; 
    put(son[a][0]);//访问左子树
    printf("%d ",a);
    put(son[a][1]);//访问右子树
}
int main()
{
    srand(20020220);
    int n=read(),m=read();
    for(int i=1;i<=n;i++)
         siz[i]=1;
    root=1; 
    for(int i=2;i<=n;i++)
        root=merge(root,i);
    for(int i=1;i<=m;i++)
    {
        int l=read(),r=read();
        fanzhuan(l,r);
    }
    put(root);
    printf("\n"); 
    return 0;
}

  使用Splay来完成

#include <cstdio>
#include <algorithm>
using namespace std;
 
const int maxn=1e5+5;
 
int n,m;
 
int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}
 
struct Splay {
    int tot,root;
    bool tag[maxn];
    int son[maxn][2];
    int siz[maxn],fa[maxn];
 
    void updata(int p) {
        siz[p]=siz[son[p][0]]+1+siz[son[p][1]];
    }
     
    void build(int l,int r,int lst) {
        if(r<l)return;
        if(l==r) {
            siz[l]=1;fa[l]=lst;
            son[lst][l>lst]=l;
            return;
        }
        int mid=(l+r)>>1;
        fa[mid]=lst,son[lst][mid>lst]=mid;
        build(l,mid-1,mid);build(mid+1,r,mid);
        updata(mid);
    }
 
    void prepare() {
        tot=n+2;build(1,n+2,0);root=(n+3)>>1;
    }
 
    void push_down(int u) {
        if(tag[u]) {
            swap(son[u][0],son[u][1]);
            tag[son[u][0]]^=1;
            tag[son[u][1]]^=1;
            tag[u]=0;
        }
    }
 
    int find(int u,int rk) {
        push_down(u);if(siz[son[u][0]]+1==rk)return u;
        if(siz[son[u][0]]>=rk)return find(son[u][0],rk);
        return find(son[u][1],rk-siz[son[u][0]]-1);
    }
 
    int t(int x) {
        return son[fa[x]][1]==x;
    }
 
    void rotate(int u) {
        int ret=t(u),f=fa[u],s=son[u][ret^1];
        son[f][ret]=s;if(s)fa[s]=f;son[u][ret^1]=f;
        fa[u]=fa[f];if(fa[f])son[fa[f]][t(f)]=u;
        fa[f]=u;updata(f),updata(u);
    }
 
    void splay(int goal,int u) {
        int tmp=fa[goal];
        while(fa[u]!=tmp) {
            if(fa[fa[u]]!=tmp) {
                if(t(fa[u])==t(u))rotate(fa[u]);
                else rotate(u);
            }
            rotate(u);
        }
        if(!tmp)root=u;
    }
 
    void rever(int l,int r) {
        int u=find(root,l-1),v=find(root,r+1);
        splay(root,u);splay(son[root][1],v);
//目标区域为root的右子结点的左子结点 tag[son[v][0]]^=1; } void run_out_ans() { for(int i=2;i<n+2;i++) printf("%d ",find(root,i)-1); } }T; int main() { n=read(),m=read(); T.prepare(); for(int i=1;i<=m;i++) { int l=read()+1,r=read()+1; T.rever(l,r); }T.run_out_ans(); return 0; }

  

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())    if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())  x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
const int N=1e5;
int n,m;
struct Splay{
    #define T(x) (tree[f[x]][1]==x)
    int tree[N+10][2],f[N+10],size[N+10];
    bool flag[N+10];
    int len,root;
    void updata(int x){size[x]=size[tree[x][0]]+size[tree[x][1]]+1;}
    void build(int x){
        root=x+1;
        for (int i=1;i<=x;i++)   f[i]=i+1,size[i]=i+1,tree[i+1][0]=i;
        tree[1][0]=x+2,f[x+2]=1,size[x+2]=1;
        size[x+1]=x+2;
    }
    void move(int x){
        int fa=f[x],son=tree[x][T(x)^1];
        tree[x][T(x)^1]=fa;
        tree[fa][T(x)]=son;
        if (son)    f[son]=fa;
        f[x]=f[fa];
        if (f[x])   tree[f[x]][T(fa)]=x;
        f[fa]=x;
        updata(fa),updata(x);
    }
    void splay(int x){
        while (f[x]){
            if (f[f[x]])    T(x)==T(f[x])?move(f[x]):move(x);
            move(x);
        }
        root=x;
    }
    void pushdown(int x){
        if (!flag[x])   return;
        swap(tree[x][0],tree[x][1]);
        flag[tree[x][0]]^=1;
        flag[tree[x][1]]^=1;
        flag[x]=0;
    }
    int find(int x,int i){
        pushdown(i);
        if (size[tree[i][0]]+1==x)  return i;
        if (x<=size[tree[i][0]]) return find(x,tree[i][0]);
        return find(x-size[tree[i][0]]-1,tree[i][1]);
    }
    void print(int x){
        if (!x) return;
        pushdown(x);
        print(tree[x][0]);
        if (x<=n)    printf("%d ",x);
        print(tree[x][1]);
    }
    void work(){
        int x=read(),y=read();
        x=find(x,root),splay(x);
        y=find(y+2,root),splay(y);
        if (f[x]!=root) move(x);
//目标区域为根的左子结点的右子树 flag[tree[x][1]]^=1; } }T; int main(){ n=read(),m=read(); T.build(n); for (int i=1;i<=m;i++) T.work(); T.print(T.root); return 0; }

  

posted @ 2020-10-20 17:15  我微笑不代表我快乐  阅读(98)  评论(0编辑  收藏  举报