zoukankan      html  css  js  c++  java
  • Splay普及版

    辣么,我要介绍我自学的(Splay)了,虽然跟大佬们讲得会有些重复,但是自认为把一些玄妙的东西点出来了(qwq)

    (0x01) 引言

    首先,我并没觉得(Splay)有多难……代码长的原因也就最多是因为不用指针太麻烦……就好像你链表不用指针而用数组模拟,在插入删除的时候就有你好受的了(qnq),更何况树形结构更为麻烦,在树上的操作也更加花样繁多。总之,麻烦。

    但是(Splay)在我眼中却更像是一种可以放诸四海而皆可用的算法,不但可以有效替代二叉搜索树、(AVL)树等数据结构,也不会由于(Treap)的随机键值而靠脸拿分(其实大多数情况下,只要没有因为被大佬%而rp--,(Treap)也是不错的选择),并且时间复杂度也是很可观的。

    那么无论怎样,在学习一种新的、我喜欢的东西之前,总是要送一句话勉励自己,并抨击那些认为这种高级数据结构没有什么学的必要的人:

    (color{cyan}{A}) (color{cyan}{person}) (color{cyan}{who}) (color{cyan}{is}) (color{cyan}{regarded}) (color{cyan}{as}) (color{cyan}{a}) (color{cyan}{loser}) (color{cyan}{isn't}) (color{cyan}{those}) (color{cyan}{ordinarys}) (color{cyan}{,but}) (color{cyan}{the}) (color{cyan}{satisfieds})

    最怕你一生庸碌无为,却总是安慰自己平凡可贵

    那么开始吧!

    一.旋转是个什么东西???

    旋转这个操作呢,在之前的数据结构中,可谓见所未见,闻所未闻(瞎扯淡(ing))。那么我们就先从旋转开始研究吧(qwq)

    由于是建立在一株二叉搜索树上的,所以当时是一条链时,旋转并不会影响树的结构(qwqqq)

    于是,二叉搜索树的旋转,完!


    诶,骗你的啦,怎么可能完,你要知道每个节点可都是还有子节点的,如果直接旋转的话,就会出现一个节点有三个子节点的情况(emmmmmm)这可不是我们想看到的,因为瞬间你的一棵(BT)就毁灭了(qwqqqq)

    那么其实为了满足(BST)的特性,我们在上面这个图里可以看到如下内容:

    根据点权来列不等式:

    (Y>X,B>X,)如果(Y<B,)那么显然B不会跑到左子树去,所以得出结论:$$Y>B$$

    所以我们可以发现,对于旋转时子节点的子树,我们完全可以将这个多余的子树转移到它原来的父节点上即可。

    那么这个地方我们就得出一个宝贵的规律来:

    我们定义一个结点与他父亲的关系是(x),那么在旋转时,他的父亲成为了他的(!x)儿子,并且那个上文中所说的“多余结点”,同时也是当前节点的(!x)儿子,但在旋转之后需要成为当前节点的“前”父节点的(x)儿子。

    诶,其实这是找规律啦,但是也从另一个方面揭示了二叉搜索树的的某些本质(qwqqq)

    (Talk) (is) (color{silver}{cheap}) (,show) (you) (the) (color{silver}{code}):

    inline void update(int x){
    	if(x){
    		sub_size[x]=cnt[x];
    		if(sons[x][1])sub_size[x]+=sub_size[sons[x][1]];
    	    if(sons[x][0])sub_size[x]+=sub_size[sons[x][0]];
    	}
    	return ;
    }
    inline bool get_which(int x){
    	return sons[f[x]][1]==x;
    }
    inline void rotate(int x){
    	int father=f[x],g_father=f[father];
    	bool which_son=get_which(x);//当前节点的关系
    	sons[father][which_son]=sons[x][which_son^1];
    	f[sons[father][which_son]]=father;
    	sons[x][which_son^1]=father;
    	f[father]=x;
    	f[x]=g_father;
    	if(g_father){
    		sons[g_father][get_which(father)]=x;
    	}
    	update(x);
    	update(father);
    }
    

    (son)表示每个节点的左右儿子,(f)表示每个节点的父亲(sub\_size[i])表示以(i)为根的子树的大小。

    诶,为什么要记录子树大小啊?

    (qwqqq)是为了方便执行之后的(zz)操作啊

    那么接下来我们看(Splay)操作,其实这个操作十分地简单,不过是拼命地向上旋转至根节点而已,但在这其中还有些地方值得注意:

    (mathcal{1.})如果爷爷节点、父节点与自己不共线,那么就是这样

    这时实际上并不会怎样……你就不断旋转就行了(qwq)

    (mathcal{2.})如果三个节点共线的话,那么就先要旋转父节点,因为如果先旋转子节点的话,我们就会发现原来华丽的一条链的结构被破坏,接下来的一系列操作即会导致这棵树失衡,所以应该先旋转父节点,再旋转子节点(qwq)

    ╮( ̄▽ ̄")╭虽然我不是很想做效果图,但是为了你们我忍了(逃

    那么接下来是当三个点共线时的两种处理方式的不同结果:

    (emmmm)实质上就是说,我们在链很长的时候,每次执行先旋父节点再旋当前节点的操作,一次总操作之后,这条链的深度会减半。

    (Talk) (is) (color{silver}{cheap}) (,show) (you) (the) (color{silver}{code}):

    inline void splay(int x){
    	for (int fa;fa=f[x];rotate(x))  
          if (f[fa])  
            rotate((get_which(x)==get_which(fa))?fa:x);  
        root=x;  
    }
    

    诶,上图画的好像不是很浅显,因为节点数太少了(qnq),但是无论如何,本蒟蒻用机房的(XP)画图做图很难受的(QAQ)



    那么接下来……那些二叉搜索树的插入删除操作我就不赘述了,因为本身二叉搜索树就可以支持找前驱后继、找排名找数,所以只需要注意以下两点:

    1.每次进行有关点的操作时都要(Splay)一次,因为要维护树的随机性

    2.注意第一条中的“有关点”,比如当给出排名找数的时候,由于其实跟这个点没什么关系,所以不用(Splay).

    (Show) (The) (Whole) (Code)

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAXN 1000000
    int f[MAXN],cnt[MAXN],value[MAXN],sons[MAXN][2],sub_size[MAXN],whole_size,root;                 
    inline int qread(){
        int res=0,k=1;
        char c=getchar();
        while(!isdigit(c)){
            if(c=='-')k=-1;
            c=getchar();
        }
        while(isdigit(c)){
            res=(res<<1)+(res<<3)+c-48;
            c=getchar();
        }
        return res*k;
    }
    inline void S_Clear(int x){
        sons[x][0]=sons[x][1]=f[x]=sub_size[x]=cnt[x]=value[x]=0; 
    }
    inline bool get_which(int x){
        return sons[f[x]][1]==x;
    }
    inline void update(int x){
        if (x){  
            sub_size[x]=cnt[x];  
            if (sons[x][0]) sub_size[x]+=sub_size[sons[x][0]];  
            if (sons[x][1]) sub_size[x]+=sub_size[sons[x][1]];  
        }  
        return ;
    }
    inline void rotate(int x){
        int father=f[x],g_father=f[father],which_son=get_which(x);
        sons[father][which_son]=sons[x][which_son^1];
        f[sons[father][which_son]]=father;
        sons[x][which_son^1]=father;
        f[father]=x;
        f[x]=g_father;
        if(g_father){
            sons[g_father][sons[g_father][1]==father]=x;
        }
        update(father);
        update(x);
    }
    inline void splay(int x){
        for (int fa;fa=f[x];rotate(x))  
          if (f[fa])  
            rotate((get_which(x)==get_which(fa))?fa:x);  
        root=x;  
    }
    inline void insert(int x){
        if(!root){
            whole_size++;
            sons[whole_size][0]=sons[whole_size][1]=f[whole_size]=0;
            root=whole_size;
            sub_size[whole_size]=cnt[whole_size]++;
            value[whole_size]=x;
            return ;
        } 
        int now=root,fa=0;
        while(1){
            if(x==value[now]){
                cnt[now]++;
                update(now);
                update(fa);
                splay(now);
                break;
            }
            fa=now;
            now=sons[now][value[now]<x];
            if(!now){
                whole_size++;
                sons[whole_size][0]=sons[whole_size][1]=0;
                f[whole_size]=fa;
                sub_size[whole_size]=cnt[whole_size]=1;
                sons[fa][value[fa]<x]=whole_size;
                value[whole_size]=x;
                update(fa);
                splay(whole_size);
                break; 
            }
        }
        
    }
    inline int find_num(int x){ 
        int now=root;
        while(1){
            if(sons[now][0]&&x<=sub_size[sons[now][0]])
            now=sons[now][0];
            else {
                int temp=(sons[now][0]?sub_size[sons[now][0]]:0)+cnt[now];
                if(x<=temp)return value[now];
                x-=temp;
                now=sons[now][1];
            }
        }
    }
    
    inline int find_rank(int x){
          int now=root,ans=0;  
        while(1){  
            if (x<value[now])  
              now=sons[now][0];  
            else{  
                ans+=(sons[now][0]?sub_size[sons[now][0]]:0);  
                if (x==value[now]){  
                    splay(now); return ans+1;  
                }  
                ans+=cnt[now];  
                now=sons[now][1];  
            }  
        }  
    }
    inline int find_pre(){
        int now=sons[root][0];
        while(sons[now][1])now=sons[now][1];
        return now;
    }
    inline int find_suffix(){
        int now=sons[root][1];
        while(sons[now][0])now=sons[now][0];
        return now;
    }
    inline void my_delete(int x){
        int hhh=find_rank(x);
        if (cnt[root]>1){
        cnt[root]--; 
        update(root); 
        return;
        }  
        if (!sons[root][0]&&!sons[root][1]) {
        S_Clear(root);
        root=0;
        return;
        }  
        if (!sons[root][0]){  
            int old_root=root; 
            root=sons[root][1];
            f[root]=0; 
            S_Clear(old_root); 
            return;  
        }  
         
        else if (!sons[root][1]){  
            int old_root=root; 
            root=sons[root][0]; 
            f[root]=0; 
            S_Clear(old_root); 
            return;  
        } 
        int left_max=find_pre(),old_root=root;  
        splay(left_max);  
        sons[root][1]=sons[old_root][1];  
        f[sons[old_root][1]]=root;  
        S_Clear(old_root);  
        update(root);  
    }
    
       
    int main(){
        int m,num,be_dealt;
        cin>>m;
        for(int i=1;i<=m;i++){
           num=qread();
           be_dealt=qread();
            switch(num)
            {
                case 1:insert(be_dealt);break;
                case 2:my_delete(be_dealt);break;
                case 3:printf("%d
    ",find_rank(be_dealt));break;
                case 4:printf("%d
    ",find_num(be_dealt));break;
                case 5:insert(be_dealt);printf("%d
    ",value[find_pre()]);my_delete(be_dealt);break;
                case 6:insert(be_dealt);printf("%d
    ",value[find_suffix()]);my_delete(be_dealt);break;
            }
        }
        return 0;
    }
    

    (0x02) (Splay)的渐进难度:

    嗯呐……我发现本蒟做这种题总会被卡……并且总是被一些奇奇怪怪的东西卡死……呃不是算法,是打代码时不细心,导致调试了好长时间(ORZ).

    那么,这道题的题面戳这里

    那么对于区间反转这种操作,我们由于原数列的顺序已经给定,所以不能按照权值排序,所以选择按照点的编号建立一棵二叉搜索树。

    诶,所以啊,不用一个个(insert)编号,我们只需要进行一下递归建树即可(qwq)——建树可以仿照线段树的建树(qwq)

    那么就类似这样:

    struct Splay_tree{
    	int f,sub_size,cnt,value,tag;
    	int son[2];
    }s[MAXN];
    inline void update(int x){
    	if(x){
    	s[x].sub_size=s[x].cnt;
    	if(s[x].son[0])s[x].sub_size+=s[s[x].son[0]].sub_size;
        if(s[x].son[1])s[x].sub_size+=s[s[x].son[1]].sub_size;
    	}
    }
    int build_tree(int l, int r, int fa) {
            if(l > r) { return 0; }
            int mid = (l + r) >> 1;
            int now = ++ wz;
            s[now].f=fa;
    	    s[now].son[0]=s[now].son[1]=0;
    		s[now].cnt++;
        	s[now].value=original[mid];
    		s[now].sub_size++;
            s[now].son[0] = build_tree(l, mid - 1, now);
            s[now].son[1] = build_tree(mid + 1, r, now);
            update(now);
            return now;
    }
    

    (emmmm)码风还算是中规中矩吧(qwq)

    那么我们现在已经有一棵编号树了(并且由于递归建树,一开始是平衡的),我们要对它进行区间翻转操作。那么实际上我们可以发现,在反转区间([l)~(r])的时候,我们可以考虑利用(Splay)的性质,将(l-1)翻转至根节点,再将(r+1)翻转至根节点的幼儿子,类似这样:

    (emmm)本蒟蒻用英文作图只是因为会使风格更简约(qwq)

    但在这里还是需要注意,我们为了方便,在(1)号节点之前和(n)号节点之后又加了两个节点并赋值为(-INF)(INF),作为虚点,既满足二叉搜索树的性质,又可以让我们在翻转(1)~(n)时不会(GG)

    那么实际上,在我们把当前区间确定下来之后,我们就要开始进行反转操作。而对于反转操作,我们可以不断替换子节点的左右子树达到此目的。

    比如对于(1)~(5)这个序列,我们反转(2~4)这个区间,过程就是这样:

    首先建树,在这里用一个可行的树来举个栗子:

    那么实际上我们如果反转(2~4)那么我们需要先将(1)(5)旋转上去,类似这样:

    实际上我们翻转两个子树就相当于反转(2)~(4) (qwq)

    但在这个地方我们可以考虑打个标记,标记的存在就只在于记录现在对于当前节点应不应该翻转两个子树。

    (color{gold}{Talk}) (is) (color{silver}{cheap}) (,color{gold}{show}) (you) (the) (color{silver}{code}):

    inline void pushdown(int x){
        if(x&&s[x].tag){
        	s[s[x].son[1]].tag^=1;
        	s[s[x].son[0]].tag^=1;
        	swap(s[x].son[1],s[x].son[0]);
        	s[x].tag=0;
        }	
    }
    inline int find(int x){
    	int now=root;
    	while(1)
    	{
    	    pushdown(now);
    		if(x<=s[s[now].son[0]].sub_size){
    			now=s[now].son[0];
    		}	
    		else  {
    		x-=s[s[now].son[0] ].sub_size + 1;
    	    if(!x)return now;
    	    now=s[now].son[1];
    		}
    	}
    }
    inline void reverse(int x,int y){
    	int l=x-1,r=y+1;
    	l=find(l),r=find(r);
    	splay(l,0);
    	splay(r,l);
    	int pos=s[root].son[1];
    	pos=s[pos].son[0];
    	s[pos].tag^=1;//标记最初打在操作区间的根节点上
    }
    

    然后还有些需要注意的,注释了(qwq)

    #include<iostream>
    using namespace std;
    #define MAXN 1000007
    #define INF 100000089
    struct Splay_tree{
    	int f,sub_size,cnt,value,tag;
    	int son[2];
    }s[MAXN];
    int original[MAXN],root,wz;
    inline bool which(int x){
    	return x==s[s[x].f].son[1];
    }
    inline void update(int x){
    	if(x){
    	s[x].sub_size=s[x].cnt;
    	if(s[x].son[0])s[x].sub_size+=s[s[x].son[0]].sub_size;
        if(s[x].son[1])s[x].sub_size+=s[s[x].son[1]].sub_size;
    	}
    }
    inline void pushdown(int x){
        if(x&&s[x].tag){
        	s[s[x].son[1]].tag^=1;
        	s[s[x].son[0]].tag^=1;
        	swap(s[x].son[1],s[x].son[0]);
        	s[x].tag=0;
        }	
    }
    inline void rotate(int x){
    	int fnow=s[x].f,ffnow=s[fnow].f;
    	pushdown(x),pushdown(fnow);
    	bool w=which(x);
    	s[fnow].son[w]=s[x].son[w^1];
    	s[s[fnow].son[w]].f=fnow;
    	s[fnow].f=x;
    	s[x].f=ffnow;
    	s[x].son[w^1]=fnow;
    	if(ffnow){
    		s[ffnow].son[s[ffnow].son[1]==fnow]=x;
    	}
    	update(fnow);
    }
    inline void splay(int x,int goal){
    	for(int qwq;(qwq=s[x].f)!=goal;rotate(x)){
    		if(s[qwq].f!=goal){//这个地方特别重要,原因是需要判断的是当前的父亲有没有到目标节点,而如果把“qwq”改成“x”……就会炸 
    			rotate(which(x)==which(qwq)?qwq:x);
    		}
    	}
    	if(goal==0){
    		root=x;
    	}
    }
    
    int build_tree(int l, int r, int fa) {
            if(l > r) { return 0; }
            int mid = (l + r) >> 1;
            int now = ++ wz;
            s[now].f=fa;
    	    s[now].son[0]=s[now].son[1]=0;
    		s[now].cnt++;
        	s[now].value=original[mid];
    		s[now].sub_size++;
            s[now].son[0] = build_tree(l, mid - 1, now);
            s[now].son[1] = build_tree(mid + 1, r, now);
            update(now);
            return now;
    }
    inline int find(int x){
    	int now=root;
    	while(1)
    	{
    	    pushdown(now);
    		if(x<=s[s[now].son[0]].sub_size){
    			now=s[now].son[0];
    		}	
    		else  {
    		x-=s[s[now].son[0] ].sub_size + 1;
    	    if(!x)return now;
    	    now=s[now].son[1];
    		}
    	}
    }
    inline void reverse(int x,int y){
    	int l=x-1,r=y+1;
    	l=find(l),r=find(r);
    	splay(l,0);
    	splay(r,l);
    	int pos=s[root].son[1];
    	pos=s[pos].son[0];
    	s[pos].tag^=1;
    }
    inline void dfs(int now){
    	pushdown(now);
    	if(s[now].son[0])dfs(s[now].son[0]);
    	if(s[now].value!=-INF&&s[now].value!=INF){
    		cout<<s[now].value<<" ";
    	}
    	if(s[now].son[1])dfs(s[now].son[1]);
    }
    int main(){
    	int n,m,x,y;
    	cin>>n>>m;
    	original[1]=-INF,original[n+2]=INF;
    	for(int i=1;i<=n;i++){
    		original[i+1]=i;
    	}
    	root=build_tree(1,n+2,0);//有一个良好的定义变量习惯很重要……重复定义同一个变量(比如全局的和局部的同名)那么就会发生覆盖。 
    	for(int i=1;i<=m;i++){
    		cin>>x>>y;
    		reverse(x+1,y+1);
    	}
    	dfs(root);
    }
    

    最后来几道例题:

    戳这里
    这个题算是平衡树系列的一个进阶版本了吧(qwq).

    好吧我承认,这个题我一开始做的时候已经想出了大约(60)%的样子,但是最后还是偷偷地瞅了眼题解,发现似乎很有道理,然后(A)(emmmm).

    那么好像很显然,我们坑定不能一个一个地修改,区间修改由于每次都是(1)~(n),所以并没有什么很大的意义。所以我们可以考虑引入一个标记,每次(A)就$$delta+=num$$每次(I)就$$insert(num-delta)$$,询问时就$$cout<<query+delta$$,再加上几个判断是不是非法操作。

    (emmmmm)好像海星,到这一步大约已经可以够得着(NOIP)的思维难度了。然而这是一道省选题,所以这么搞还是捕星,因为删除操作和询问离职人员好像很难搞。

    那么,在这里我们考虑将(-INF)(INF)在一开始就插入平衡树里。每次(insert)操作(tot++),最后只需要用$$tot-(find_rank(INF)-2)$$就可以算出离职人数。

    那么只剩下最后一个问题了——我们该怎么删除呢?这里就需要用到一个二叉搜索树里很精妙的操作了——删除根的子树。我们可以在执行(S)操作时先(delta-=num),然后将(-INF)旋转到根节点,将(minn-delta)旋转到根节点的右儿子,然后删除根节点的右子树的左子树即可。

    诶这个操作很熟悉啊,不也是区间翻转时的操作吗?

    诶怎么旋转(minn-delta)啊?

    我们可以很简单地插入删除,这很简单,但是为什么要以(minn-delta)作为所删除的单调区间的上界?这个地方你可以稍微意会一下……因为我们插入的是(num-delta)啊!

    嗯,我还是太弱了。

    (color{red}{Code}:)

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAXN 1000000
    #define INF 283653129 
    int f[MAXN],cnt[MAXN],value[MAXN],sons[MAXN][2],sub_size[MAXN],whole_size,root;                 
    inline int qread(){
        int res=0,k=1;
        char c=getchar();
        while(!isdigit(c)){
            if(c=='-')k=-1;
            c=getchar();
        }
        while(isdigit(c)){
            res=(res<<1)+(res<<3)+c-48;
            c=getchar();
        }
        return res*k;
    }
    inline void S_Clear(int x){
        sons[x][0]=sons[x][1]=f[x]=sub_size[x]=cnt[x]=value[x]=0; 
    }
    inline bool get_which(int x){
        return sons[f[x]][1]==x;
    }
    inline void update(int x){
        if (x){  
            sub_size[x]=cnt[x];  
            if (sons[x][0]) sub_size[x]+=sub_size[sons[x][0]];  
            if (sons[x][1]) sub_size[x]+=sub_size[sons[x][1]];  
        }  
        return ;
    }
    inline void rotate(int x){
        int father=f[x],g_father=f[father],which_son=get_which(x);
        sons[father][which_son]=sons[x][which_son^1];
        f[sons[father][which_son]]=father;
        sons[x][which_son^1]=father;
        f[father]=x;
        f[x]=g_father;
        if(g_father){
            sons[g_father][sons[g_father][1]==father]=x;
        }
        update(father);
        update(x);
    }
    inline void splay(int x,int goal){
        for(int qwq;(qwq=f[x])!=goal;rotate(x)){
            if(f[qwq]!=goal){
                rotate(get_which(x)==get_which(qwq)?qwq:x);
            }
        } 
        if(!goal){
            root=x;
        }
    }
    inline void insert(int x){
        if(!root){
            whole_size++;
            sons[whole_size][0]=sons[whole_size][1]=f[whole_size]=0;
            root=whole_size;
            sub_size[whole_size]=cnt[whole_size]++;
            value[whole_size]=x;
            return ;
        } 
        int now=root,fa=0;
        while(1){
            if(x==value[now]){
                cnt[now]++;
                update(now);
                update(fa);
                splay(now,0);
                break;
            }
            fa=now;
            now=sons[now][value[now]<x];
            if(!now){
                whole_size++;
                sons[whole_size][0]=sons[whole_size][1]=0;
                f[whole_size]=fa;
                sub_size[whole_size]=cnt[whole_size]=1;
                sons[fa][value[fa]<x]=whole_size;
                value[whole_size]=x;
                update(fa);
                splay(whole_size,0);
                break; 
            }
        }
        
    }
    inline int find_num(int x){ 
        int now=root;
        while(1){
            if(sons[now][0]&&x<=sub_size[sons[now][0]])
            now=sons[now][0];
            else {
                int temp=(sons[now][0]?sub_size[sons[now][0]]:0)+cnt[now];
                if(x<=temp)return value[now];
                x-=temp;
                now=sons[now][1];
            }
        }
    }
    inline int find_ID(int x){
    	int now=root;
    	while(1){
    		if(x==value[now]){
    			return now;
    		}
    		else now=sons[now][value[now]<x]; 
    	}
    }
    inline int find_rank(int x){
          int now=root,ans=0;  
        while(1){  
            if (x<value[now])  
              now=sons[now][0];  
            else{  
                ans+=(sons[now][0]?sub_size[sons[now][0]]:0);  
                if (x==value[now]){  
                    splay(now,0); return ans+1;  
                }  
                ans+=cnt[now];  
                now=sons[now][1];  
            }  
        }  
    }
    inline int find_pre(){
        int now=sons[root][0];
        while(sons[now][1])now=sons[now][1];
        return now;
    }
    inline int find_suffix(){
        int now=sons[root][1];
        while(sons[now][0])now=sons[now][0];
        return now;
    }
    inline void my_delete(int x){
        int hhh=find_rank(x);
        if (cnt[root]>1){
        cnt[root]--; 
        update(root); 
        return;
        }  
        if (!sons[root][0]&&!sons[root][1]) {
        S_Clear(root);
        root=0;
        return;
        }  
        if (!sons[root][0]){  
            int old_root=root; 
            root=sons[root][1];
            f[root]=0; 
            S_Clear(old_root); 
            return;  
        }  
         
        else if (!sons[root][1]){  
            int old_root=root; 
            root=sons[root][0]; 
            f[root]=0; 
            S_Clear(old_root); 
            return;  
        } 
        int left_max=find_pre(),old_root=root;  
        splay(left_max,0);  
        sons[root][1]=sons[old_root][1];  
        f[sons[old_root][1]]=root;  
        S_Clear(old_root);  
        update(root);  
    }
    int main(){
        int m,num,minn;
        char a;
        cin>>m>>minn;
        insert(-INF);
    	insert(INF); 
    	int delta=0,sumtot=0;
        for(int i=1;i<=m;i++){
        	cin>>a>>num;
        	switch(a){
        		case 'I' :{
        			if(num<minn)break;
        			insert(num-delta);
        			sumtot++;
        			break;
        		} 
        		case 'A':{
        			delta+=num;
        			break;
        		}
        		case 'S':{
        			delta-=num;
        			insert(minn-delta);
        			int a=find_ID(-INF),b=find_ID(minn-delta);
    				splay(a,0);
        			splay(b,a);
        			sons[sons[root][1]][0]=0; 
        			my_delete(minn-delta);
        			break;
        		}
        		case 'F':{
        			int sumnow=find_rank(INF)-2;
        			if(sumnow<num){
        			cout<<-1<<endl;	
    				break;
        	  		}
    				int  res=find_num(sumnow+2-num);
        			cout<<res+delta<<endl;
        			break;
        		}
        	}
        }
        int sumnow=find_rank(INF)-2;  
        cout<<sumtot-sumnow;
    	return 0;
    }
    

    代码好长啊……足足写了5.04K

  • 相关阅读:
    js dom
    js Number string
    jq ajax数据交互
    js date 和 math
    js中英文网页切换
    日常使用
    php求和
    empty()
    时间戳、日期相互转换
    数组转字符串之间相互转换
  • 原文地址:https://www.cnblogs.com/pks-t/p/9062505.html
Copyright © 2011-2022 走看看