zoukankan      html  css  js  c++  java
  • 洛谷平衡树模板总结

    可持久化文艺平衡树

    您需要写一种数据结构,来维护一个序列,其中需要提供以下操作(对于各个以往的历史版本):

    1. 在第 pp 个数后插入数 xx 。
    2. 删除第 pp 个数。
    3. 翻转区间 [l,r][l,r],例如原序列是 {5,4,3,2,1}{5,4,3,2,1},翻转区间 [2,4][2,4] 后,结果是 {5,2,3,4,1}{5,2,3,4,1}。
    4. 查询区间 [l,r][l,r] 中所有数的和。

    和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本(操作 44 即保持原版本无变化),新版本即编号为此次操作的序号。

    本题强制在线。

    分析

    函数式Treap实现。

    时空复杂度(O(n log n))

    // luogu-judger-enable-o2
    typedef long long ll;
    
    co int N=2e5+7;
    int tot;
    int root[N];
    int can[N],cantop;
    namespace T
    {
        using std::swap;
        
        int ch[N<<6][2],siz[N<<6];
        int pri[N<<6],val[N<<6];
        ll sum[N<<6];
        bool rev[N<<6];
        
        int newnode(int v=0)
        {
            int x=cantop?can[cantop--]:++tot;
            ch[x][0]=ch[x][1]=0,siz[x]=1;
            pri[x]=rand()<<15|rand(),val[x]=sum[x]=v;
            rev[x]=0;
            return x;
        }
        
        int clone(int y)
        {
            int x=cantop?can[cantop--]:++tot;
            ch[x][0]=ch[y][0],ch[x][1]=ch[y][1],siz[x]=siz[y];
            pri[x]=pri[y],val[x]=val[y],sum[x]=sum[y];
            rev[x]=rev[y];
            return x;
        }
        
        void pushup(int x)
        {
            siz[x]=siz[ch[x][0]]+1+siz[ch[x][1]];
            sum[x]=sum[ch[x][0]]+val[x]+sum[ch[x][1]];
        }
        
        void pushdown(int x)
        {
            if(rev[x])
            {
                swap(ch[x][0],ch[x][1]);
                if(ch[x][0])
                {
                    ch[x][0]=clone(ch[x][0]);
                    rev[ch[x][0]]^=1;
                }
                if(ch[x][1])
                {
                    ch[x][1]=clone(ch[x][1]);
                    rev[ch[x][1]]^=1;
                }
                rev[x]=0;
            }
        }
        
        void split(int x,int k,int&l,int&r)
        {
            if(!x)
            {
                l=r=0;
                return;
            }
            pushdown(x);
            if(k<=siz[ch[x][0]])
            {
                r=clone(x);
                split(ch[r][0],k,l,ch[r][0]);
                pushup(r);
            }
            else
            {
                l=clone(x);
                split(ch[l][1],k-siz[ch[x][0]]-1,ch[l][1],r);
                pushup(l);
            }
        }
        
        int merge(int x,int y)
        {
            if(!x||!y)
                return x+y;
            if(pri[x]<pri[y])
            {
                pushdown(y);
                ch[y][0]=merge(x,ch[y][0]);
                pushup(y);
                return y;
            }
            else
            {
                pushdown(x);
                ch[x][1]=merge(ch[x][1],y);
                pushup(x);
                return x;
            }
        }
        
        void insert(int&rt,int k,int v)
        {
            int x,y;
            split(rt,k,x,y);
            rt=merge(x,merge(newnode(v),y));
        }
        
        void erase(int&rt,int p)
        {
            int x,y,z;
            split(rt,p,x,z);
            split(x,p-1,x,y);
            can[++cantop]=y;
            rt=merge(x,z);
        }
        
        void reverse(int&rt,int l,int r)
        {
            int x,y,z;
            split(rt,r,x,z);
            split(x,l-1,x,y);
            rev[y]^=1;
            rt=merge(x,merge(y,z));
        }
        
        ll query(int&rt,int l,int r)
        {
            int x,y,z;
            split(rt,r,x,z);
            split(x,l-1,x,y);
            ll ans=sum[y];
            rt=merge(x,merge(y,z));
            return ans;
        }
    }
    using namespace T;
    using namespace std;
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
        int n=read<int>();
        ll lastans=0;
        for(int i=1;i<=n;++i)
        {
            int v,q;
            read(v),read(q);
            root[i]=root[v];
            if(q==1)
            {
                int p,x;
                read(p),read(x);
                p^=lastans,x^=lastans;
                insert(root[i],p,x);
            }
            else if(q==2)
            {
                int p;
                read(p);
                p^=lastans;
                erase(root[i],p);
            }
            else if(q==3)
            {
                int l,r;
                read(l),read(r);
                l^=lastans,r^=lastans;
                reverse(root[i],l,r);
            }
            else
            {
                int l,r;
                read(l),read(r);
                l^=lastans,r^=lastans;
                printf("%lld
    ",lastans=query(root[i],l,r));
            }
        }
        return 0;
    }
    

    可持久化平衡树

    您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本):

    1. 插入x数
    2. 删除x数(若有多个相同的数,因只删除一个,如果没有请忽略该操作)
    3. 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
    4. 查询排名为x的数
    5. 求x的前驱(前驱定义为小于x,且最大的数,如不存在输出-2147483647)
    6. 求x的后继(后继定义为大于x,且最小的数,如不存在输出2147483647)

    和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本。(操作3, 4, 5, 6即保持原版本无变化)

    每个版本的编号即为操作的序号(版本0即为初始状态,空树)

    (n leq 5 imes 10^5)

    分析

    可以发现非旋Treap的split和merge每次变动的都是一条链。

    然后就对这条链可持久化一下就行了。

    时空复杂度(O(n log n))

    Hint

    注意copy的部分仅限于递归处理的时候,now=0,x=0,y=0这些时候就不用可持久化了,不然会莫名其妙地错。

    然后是空间问题,其实3、4、5、6操作不用可持久化,但是平衡树能A就行了,论效率平衡树肯定赶不上其他的做法。

    co int MAXN=5e5*50,MAXM=5e5+7; // edit 2
    
    int root[MAXN],tot;
    struct Treap
    {
    	int ch[MAXN][2],siz[MAXN];
    	int val[MAXN],pri[MAXN];
    	
    	il int newnode(rg int v=0)
    	{
    		++tot;
    		ch[tot][0]=ch[tot][1]=0,siz[tot]=1;
    		val[tot]=v,pri[tot]=rand()|rand()<<16;
    		return tot;
    	}
    	
    	il void pushup(rg int now)
    	{
    		siz[now]=siz[ch[now][0]]+1+siz[ch[now][1]];
    	}
    	
    	il void copy(rg int x,rg int y)
    	{
    		ch[x][0]=ch[y][0],ch[x][1]=ch[y][1],siz[x]=siz[y];
    		val[x]=val[y],pri[x]=pri[y];
    	}
    	
    	il void split(rg int now,rg int v,rg int&x,rg int&y)
    	{
    		if(!now)
    		{
    			x=y=0;
    			return;
    		}
    		if(val[now]<=v)
    		{
    			x=newnode();
    			copy(x,now);
    			split(ch[x][1],v,ch[x][1],y);
    			pushup(x);
    		}
    		else
    		{
    			y=newnode();
    			copy(y,now);
    			split(ch[y][0],v,x,ch[y][0]);
    			pushup(y);
    		}
    	}
    	
    	il int merge(rg int x,rg int y)
    	{
    		if(!x||!y) // edit 1
    			return x+y;
    		rg int now=newnode();
    		if(pri[x]<pri[y])
    		{
    			copy(now,x);
    			ch[now][1]=merge(ch[now][1],y);
    			pushup(now);
    		}
    		else
    		{
    			copy(now,y);
    			ch[now][0]=merge(x,ch[now][0]);
    			pushup(now);
    		}
    		return now;
    	}
    	
    	il void ins(rg int&now,rg int v)
    	{
    		rg int x,y;
    		split(now,v,x,y);
    		now=merge(x,merge(newnode(v),y));
    	}
    	
    	il void del(rg int&now,rg int v)
    	{
    		rg int x,y,z;
    		split(now,v-1,x,y);
    		split(y,v,y,z);
    		y=merge(ch[y][0],ch[y][1]);
    		now=merge(x,merge(y,z));
    	}
    	
    	il int rank(rg int&now,rg int v)
    	{
    		rg int x,y;
    		split(now,v-1,x,y);
    		rg int ans=siz[x]+1;
    		now=merge(x,y);
    		return ans;
    	}
    	
    	il int kth(rg int now,rg int k)
    	{
    		if(!now)
    			return 0;
    		while(k)
    		{
    			if(siz[ch[now][0]]>=k)
    				now=ch[now][0];
    			else if(siz[ch[now][0]]+1==k)
    				return now;
    			else
    			{
    				k-=siz[ch[now][0]]+1;
    				now=ch[now][1];
    			}
    		}
    		return now;
    	}
    	
    	il int pre(rg int&now,rg int v)
    	{
    		rg int x,y;
    		split(now,v-1,x,y);
    		rg int ans=kth(x,siz[x]);
    		now=merge(x,y);
    		return ans;
    	}
    	
    	il int suc(rg int&now,rg int v)
    	{
    		rg int x,y;
    		split(now,v,x,y);
    		rg int ans=kth(y,1);
    		now=merge(x,y);
    		return ans;
    	}
    }T;
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	srand(20030506);
    	rg int n;
    	read(n);
    	for(rg int i=1;i<=n;++i)
    	{
    		rg int v,opt,x;
    		read(v);read(opt);read(x);
    //		cerr<<"v="<<v<<" opt="<<opt<<" x="<<x<<endl;
    		root[i]=root[v];
    		if(opt==1) // insert
    		{
    			T.ins(root[i],x);
    		}
    		else if(opt==2) // delete
    		{
    			T.del(root[i],x);
    		}
    		else if(opt==3) // rank
    		{
    			printf("%d
    ",T.rank(root[i],x));
    		}
    		else if(opt==4) // kth
    		{
    			printf("%d
    ",T.val[T.kth(root[i],x)]);
    		}
    		else if(opt==5) // pre
    		{
    			int ans=T.pre(root[i],x);
    			if(ans==0)
    				puts("-2147483647");
    			else
    				printf("%d
    ",T.val[ans]);
    		}
    		else if(opt==6) // suf
    		{
    			int ans=T.suc(root[i],x);
    			if(ans==0)
    				puts("2147483647");
    			else
    				printf("%d
    ",T.val[ans]);
    		}
    	}
    	return 0;
    }
    

    再分析

    然而对这题而言有更优的做法,主席树(可持久化权值线段树)。

    将权值离散化,得到了这题较优的做法。

    但是要离线,所以也是个问题。不离线的话空间会大一些,问题不大。

    时间复杂度(O(n log n)),常数小多了。

    Hint

    注意调用查询的时候,边界问题。

    另外“若有多个相同的数,因只删除一个,如果没有请忽略该操作”。这个神坑点卡了我好久,非旋式Treap会自动忽略不存在的,所以就没管。

    co int MAXN=5e5*20,MAXM=5e5+7;
    
    int v[MAXN],opt[MAXN],x[MAXN];
    vector<int>xlist;
    
    int root[MAXM],tot;
    struct SegTree
    {
    	int sumv[MAXN];
    	int L[MAXN],R[MAXN];
    	
    	il void pushup(rg int now)
    	{
    		sumv[now]=sumv[L[now]]+sumv[R[now]];
    //		assert(sumv[now]>=0);
    	}
    	
    	il void copy(rg int x,rg int y)
    	{
    		sumv[x]=sumv[y];
    		L[x]=L[y],R[x]=R[y];
    	}
    	
    	il void modify(rg int&now,rg int l,rg int r,rg int p,rg int v)
    	{
    		++tot;
    		copy(tot,now);
    		now=tot;
    		if(l==r)
    		{
    			sumv[now]+=v;
    			return;
    		}
    		rg int m=(l+r)>>1;
    		if(p<=m)
    			modify(L[now],l,m,p,v);
    		else
    			modify(R[now],m+1,r,p,v);
    		pushup(now);
    	}
    	
    	il int sum(rg int now,rg int l,rg int r,rg int ql,rg int qr)
    	{
    		if(ql<=l&&r<=qr)
    			return sumv[now];
    		rg int m=(l+r)>>1;
    		if(qr<=m)
    			return sum(L[now],l,m,ql,qr);
    		if(ql>=m+1)
    			return sum(R[now],m+1,r,ql,qr);
    		return sum(L[now],l,m,ql,qr)+sum(R[now],m+1,r,ql,qr);
    	}
    	
    	il int kth(rg int now,rg int l,rg int r,rg int k)
    	{
    		if(l==r)
    			return l;
    		rg int m=(l+r)>>1;
    		if(sumv[L[now]]>=k)
    			return kth(L[now],l,m,k);
    		else
    		{
    			k-=sumv[L[now]];
    			return kth(R[now],m+1,r,k);
    		}
    	}
    	
    	il int pre(rg int root,rg int p)
    	{
    		rg int num=p>1?sum(root,1,xlist.size(),1,p-1):0; // edit 1:notice p=1
    		if(num==0) // do not exist
    			return 0;
    		else
    			return kth(root,1,xlist.size(),num);
    	}
    	
    	il int suc(rg int root,rg int p)
    	{
    		rg int num=p<xlist.size()?sum(root,1,xlist.size(),p+1,xlist.size()):0; // edit 2:notice p=xlist.size()
    		if(num==0) // do not exist
    			return xlist.size()+1;
    		else
    			return kth(root,1,xlist.size(),sumv[root]-num+1);
    	}
    }T;
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	rg int n;
    	read(n);
    	for(rg int i=1;i<=n;++i)
    	{
    		read(v[i]);read(opt[i]);read(x[i]);
    		if(opt[i]!=4) // unless kth
    			xlist.push_back(x[i]);
    	}
    	sort(xlist.begin(),xlist.end());
    	xlist.erase(unique(xlist.begin(),xlist.end()),xlist.end());
    	for(rg int i=1;i<=n;++i)
    	{
    		if(opt[i]!=4)
    			x[i]=lower_bound(xlist.begin(),xlist.end(),x[i])-xlist.begin()+1;
    //		cerr<<"x "<<i<<" = "<<x[i]<<endl;
    		root[i]=root[v[i]];
    		if(opt[i]==1) // insert
    		{
    			T.modify(root[i],1,xlist.size(),x[i],1);
    		}
    		else if(opt[i]==2) // delete
    		{
    			// edit 3:if this val do not exist, you should ignore this operation
    			if(T.sum(root[i],1,xlist.size(),x[i],x[i])==0)
    				continue;
    			T.modify(root[i],1,xlist.size(),x[i],-1);
    		}
    		else if(opt[i]==3) // rank
    		{
    			printf("%d
    ",1+(x[i]>1?T.sum(root[i],1,xlist.size(),1,x[i]-1):0)); // edit 1: notice x[i]=1
    		}
    		else if(opt[i]==4) // kth
    		{
    //			assert(1<=x[i]&&x[i]<=T.sumv[root[i]]);
    			printf("%d
    ",xlist[T.kth(root[i],1,xlist.size(),x[i])-1]);
    		}
    		else if(opt[i]==5) // pre
    		{
    			int ans=T.pre(root[i],x[i]);
    			if(ans!=0)
    				printf("%d
    ",xlist[ans-1]);
    			else
    				puts("-2147483647");
    		}
    		else if(opt[i]==6) // suc
    		{
    			int ans=T.suc(root[i],x[i]);
    			if(ans!=xlist.size()+1)
    				printf("%d
    ",xlist[ans-1]);
    			else
    				puts("2147483647");
    		}
    	}
    	return 0;
    }
    

    三分析

    其实树状数组也可以做,并且常数更小。

    但是空间就必须提前开出来,并且不离线不行了。

    然后不用可持久化,可以搞一个dfs。给时间点连上边,dfs的时候就修改+撤销就行了。

    第一次知道这么精妙的做法,那线段树、平衡树貌似都可以这么搞,并且空间复杂度大为减小。

    分享一下洛谷全站最快代码,by Mr_Spade

    
    const int N=5e5+5;
    int n,m,lgn;
    int num[N],tot;
    int bit[N];
    inline void add(int x,int k)
    {
        while(x<=n)
            bit[x]+=k,x+=x&-x;
        return;
    }
    inline int ask(int x)
    {
        int res=0;
        while(x)
            res+=bit[x],x&=x-1;
        return res;
    }
    inline int find(int x)
    {
        int res=0;
        for(int i=lgn;~i;i--)
            if((res|1<<i)<=n&&bit[res|1<<i]<x)
                x-=bit[res|=1<<i];
        return res+1;
    }
    struct oper
    {
        int opt,x;
    }o[N];
    int first[N],next[N];
    int ans[N];
    void dfs(int now)
    {
        int d=0;
        register int go;
        for(go=first[now];go;go=next[go])
        {
            switch(o[go].opt)
            {
                case 1:add(o[go].x,1);break;
                case 2:
                    if(ask(o[go].x)^ask(o[go].x-1))
                        add(o[go].x,-1);
                    else
                        d=1;
                    break;
                case 3:ans[go]=ask(o[go].x-1)+1;break;
                case 4:ans[go]=num[find(o[go].x)];break;
                case 5:
                    if(!(d=ask(o[go].x-1)))
                        ans[go]=-0x7fffffff;
                    else
                        ans[go]=num[find(d)];
                    break;
                case 6:
                    if((d=ask(o[go].x))==ask(n))
                        ans[go]=0x7fffffff;
                    else
                        ans[go]=num[find(d+1)];
                    break;
            }
            dfs(go);
            switch(o[go].opt)
            {
                case 1:add(o[go].x,-1);break;
                case 2:
                    if(!d)
                        add(o[go].x,1);
                    break;
            }
        }
        return;
    }
    signed main()
    {
        fseek(stdin,0l,2);
        int len=ftell(stdin);
        rewind(stdin);
        fread(in,1,len,stdin);
        int x;
        register int i;
        m=read();
        for(i=1;i<=m;i++)
        {
            next[i]=first[x=read()];first[x]=i;
            o[i].opt=read();o[i].x=read();
            if(o[i].opt^4)
                num[++tot]=o[i].x;
        }
        sort(num+1,num+tot+1);
        n=unique(num+1,num+tot+1)-num-1;
        for(lgn=1;1<<lgn<=n;lgn++);lgn--;
        for(i=1;i<=m;i++)
            if(o[i].opt^4)
                o[i].x=lower_bound(num+1,num+n+1,o[i].x)-num;
        dfs(0);
        for(i=1;i<=m;i++)
            if(o[i].opt!=1&&o[i].opt!=2)
                write(ans[i]),putchar('
    ');
        fwrite(out,1,fout,stdout);
        return 0;
    }
    

    这题的bug

    正如上面看到的,这题可以乱搞。所以出题人要是想考察可持久化平衡树的话,最好还是强制在线,另外为了卡掉线段树还有01Trie,可以把节点弄成双键值,比如一个pair。

    另外一个有趣的问题:持久化fhq treap在合并时不需要复制节点。正如我所说的:“这跟这题的单点插入有关,如果是单点插入那么相当于要插入的新节点无论如何都复制好了,换句话说隐式的新建了一个节点。但是如果是插入一段新节点的话就错了,因为这段的新节点没有隐式复制。”

    讨论

    文艺平衡树(Splay)

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

    (n,m leq 1e5)

    分析

    Splay Tree实现。

    时间复杂度均摊(O(log n))

    typedef long long ll;
    
    co int N=1e5+7;
    namespace T
    {
    	using std::swap;
    	
    	int rt;
    	int fa[N],ch[N][2],siz[N];
    	bool rev[N];
    	
    	void pushup(int x)
    	{
    		siz[x]=siz[ch[x][0]]+1+siz[ch[x][1]];
    	}
    	
    	void pushdown(int x)
    	{
    		if(rev[x])
    		{
    			swap(ch[x][0],ch[x][1]);
    			rev[ch[x][0]]^=1;
    			rev[ch[x][1]]^=1;
    			rev[x]=0;
    		}
    	}
    	
    	void rotate(int x,int&k)
    	{
    		int y=fa[x],z=fa[y],d=ch[y][0]==x;
    		if(y==k)
    			k=x;
    		else
    		{
    			if(ch[z][0]==y)
    				ch[z][0]=x;
    			else
    				ch[z][1]=x;
    		}
    		ch[y][d^1]=ch[x][d],fa[ch[y][d^1]]=y;
    		ch[x][d]=y,fa[y]=x,fa[x]=z;
    		pushup(x);
    		pushup(y);
    	}
    	
    	void splay(int x,int&k)
    	{
    		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);
    		}
    	}
    	
    	void build(int l,int r,int f)
    	{
    		if(l>r)
    			return;
    		int mid=(l+r)/2;
    		if(mid<f)
    			ch[f][0]=mid;
    		else
    			ch[f][1]=mid;
    		fa[mid]=f,siz[mid]=1;
    		if(l==r)
    			return;
    		build(l,mid-1,mid);
    		build(mid+1,r,mid);
    		pushup(mid);
    	}
    	
    	int find(int x,int k)
    	{
    		pushdown(x);
    		int s=siz[ch[x][0]];
    		if(k==s+1)
    			return x;
    		if(k<=s)
    			return find(ch[x][0],k);
    		else
    			return find(ch[x][1],k-s-1);
    	}
    	
    	void rever(int l,int r)
    	{
    		int x=find(rt,l),y=find(rt,r+2);
    		splay(x,rt);
    		splay(y,ch[x][1]);
    		int z=ch[y][0];
    		rev[z]^=1;
    	}
    }
    using namespace T;
    using namespace std;
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	int n,m;
    	read(n),read(m);
    	rt=(n+3)/2;
    	build(1,n+2,rt);
    	while(m--)
    	{
    		int l,r;
    		read(l),read(r);
    		rever(l,r);
    	}
    	for(int i=2;i<=n+1;++i)
    		printf("%d ",find(rt,i)-1);
    	return 0;
    }
    

    普通平衡树

    您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

    1. 插入x数
    2. 删除x数(若有多个相同的数,因只删除一个)
    3. 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
    4. 查询排名为x的数
    5. 求x的前驱(前驱定义为小于x,且最大的数)
    6. 求x的后继(后继定义为大于x,且最小的数)

    (n leq 10^5)

    分析

    用范浩强Treap实现。具体原理:










    范浩强对函数式编程在OI中的应用做了很好的引入工作。

    非旋式Treap的精华在于那个merge。
    merge的参数要求保证x中最大的数不大于y中最小的数。
    这样在合并一个子树的时候,有两种等价情况,一种是x是y的左儿子,一种是y是x的右儿子。
    选择的依据是priority,这样平衡的道理就跟普通Treap一样了。

    const int INF=0x7fffffff;
    
    const int MAXN=1e5+7;
    
    int sz;
    struct Treap
    {
    	int val[MAXN],pri[MAXN];
    	int ch[MAXN][2],siz[MAXN];
    	
    	void pushup(int x)
    	{
    		siz[x]=siz[ch[x][0]]+1+siz[ch[x][1]];
    	}
    	
    	int new_node(int v)
    	{
    		val[++sz]=v,
    		pri[sz]=rand()<<15|rand();
    		ch[sz][0]=ch[sz][1]=0,
    		siz[sz]=1;
    		return sz;
    	}
    	
    	int merge(int x,int y)
    	{
    		if(!x||!y)
    			return x+y;
    		if(pri[x]<pri[y])
    		{
    			ch[x][1]=merge(ch[x][1],y);
    			pushup(x);
    			return x;
    		}
    		else
    		{
    			ch[y][0]=merge(x,ch[y][0]);
    			pushup(y);
    			return y;
    		}
    	}
    	
    	void split(int now,int v,int&x,int&y)
    	{
    		if(!now)
    			x=y=0;
    		else
    		{
    			if(val[now]<=v)
    			{
    				x=now,split(ch[now][1],v,ch[now][1],y);
    			}
    			else
    			{
    				y=now,split(ch[now][0],v,x,ch[now][0]);
    			}
    			pushup(now);
    		}
    	}
    	
    	void ins(int&now,int v)
    	{
    		int x,y;
    		split(now,v,x,y);
    		now=merge(merge(x,new_node(v)),y);
    	}
    	
    	void del(int&now,int v)
    	{
    		int x,y,z;
    		split(now,v,x,z);
    		split(x,v-1,x,y);
    		y=merge(ch[y][0],ch[y][1]);
    		now=merge(merge(x,y),z);
    	}
    	
    	int rank(int&now,int v)
    	{
    		int x,y;
    		split(now,v-1,x,y);
    		int ans=siz[x]+1;
    		now=merge(x,y);
    		return ans;
    	}
    	
    	int kth(int now,int k)
    	{
    		while(1)
    		{
    			if(k<=siz[ch[now][0]])
    				now=ch[now][0];
    			else if(k==siz[ch[now][0]]+1)
    				return now;
    			else
    				k-=siz[ch[now][0]]+1,now=ch[now][1];
    		}
    	}
    	
    	int pre(int&now,int v)
    	{
    		int x,y;
    		split(now,v-1,x,y);
    		int ans=kth(x,siz[x]);
    		now=merge(x,y);
    		return ans;
    	}
    	
    	int suc(int&now,int v)
    	{
    		int x,y;
    		split(now,v,x,y);
    		int ans=kth(y,1);
    		now=merge(x,y);
    		return ans;
    	}
    }T;
    
    int main()
    {
    //  freopen(".in","r",stdin);
    //  freopen(".out","w",stdout);
    	srand(20030506);
    	int root=0;
    	int n;
    	read(n);
    	while(n--)
    	{
    		static int opt,x;
    		read(opt);read(x);
    		if(opt==1) // insert
    		{
    			T.ins(root,x);
    		}
    		else if(opt==2) // delete
    		{
    			T.del(root,x);
    		}
    		else if(opt==3) // rank
    		{
    			printf("%d
    ",T.rank(root,x));
    		}
    		else if(opt==4) // kth
    		{
    			printf("%d
    ",T.val[T.kth(root,x)]);
    		}
    		else if(opt==5) // precursor
    		{
    			printf("%d
    ",T.val[T.pre(root,x)]);
    		}
    		else // successor
    		{
    			printf("%d
    ",T.val[T.suc(root,x)]);
    		}
    	}
    //  fclose(stdin);
    //  fclose(stdout);
        return 0;
    }
    
  • 相关阅读:
    PHP 文件上传
    浅析文件上传漏洞
    JS之Number类
    JS之String类
    Java中的多态
    JS之数据类型
    JavaScript的组成
    双向链表与LRU算法实现
    字符串比较--小问题大智慧
    龙生九子-浅谈Java的继承
  • 原文地址:https://www.cnblogs.com/autoint/p/treap.html
Copyright © 2011-2022 走看看