1 条题解

  • 0
    @ 2025-8-24 21:59:48

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 541forever
    exlg伪犇验证

    搬运于2025-08-24 21:59:48,当前版本为作者最后更新于2022-04-05 10:39:16,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    这是一道比较裸的平衡树模板题,这是一篇可能对细(keng)节(dian)解释比较详尽的屑题解。

    思路与 文艺平衡树 相似,我们需要先增加两个哨兵节点,然后每次执行区间翻转操作时,就对区间左端点的前驱和右端点的后继进行 Splay 并把不断交换他们子节点的左右子树。而对于求解答案,我们要求的是这个节点的排名(即它在当前序列中的位置),我们可以将该节点提到根,然后统计它的左子树的大小 size(这个 size 没有算上这个节点本身,但算上了哨兵节点,所有两个影响抵消)。具体实现时,我们可以记录每个权值在平衡树中对应的节点编号记为 pos[i],然后每次翻转时找到当前节点 pos[i] 的后继,而在我们进行这一步操作前,将要操作的值前面的数已经是有序的了,所有它的前驱便是 pos[i-1]

    注意:

    1. 我们不太能直接以编号作为平衡树的权值进行操作,这样虽然仍能实现区间翻转,但查一个数在序列中的位置会较为麻烦。
    2. 区间翻转需要提到根上的点时左端点的前驱和右端点的后继,而不是左端点和右端点(本蒟蒻就是因为这里一直不知道哪错了)。
    3. 给定数组的值可能会有相等而我们需要按输入的原始次序操作,所以我们可以将原序列重新按离散的方式重新赋值。

    下面是丑陋的代码:

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    int n,val[300005],tag[300005],ch[300005][2],tot,rt,fa[300005],si[300005],p[300005];//p记录权值在平衡树中对应的点的编号,tag作为懒标记记录是否需要交换左右子树
    struct object{
        int val,num;
    }ob[300005];
    inline int read(){
    	int x=0,f=1;
    	char ch;
    	do{
    		ch=getchar();
    		if(ch=='-'){
    			f=-1;
    		}
    	}while(!(ch>='0'&&ch<='9'));
    	while(ch>='0'&&ch<='9'){
    		x=(x<<1)+(x<<3)+ch-'0';
    		ch=getchar();
    	}
    	return x*f;
    }
    struct Splay{
        void push_up(int cur){
            si[cur]=si[ch[cur][1]]+si[ch[cur][0]]+1;
        }
        int get(int x){
            return x==ch[fa[x]][1];
        }
        int build(int l,int r,int f){
            if(l>r){
                return 0;
            }
            int mid=(l+r)>>1;
            int now=++tot;
            fa[now]=f;
            val[now]=ob[mid].val;
            p[ob[mid].val]=now;
            ch[now][0]=build(l,mid-1,now);
            ch[now][1]=build(mid+1,r,now);
            push_up(now);
            return now;
        }
        void push_down(int cur){
            if(tag[cur]){
                tag[cur]=0;
                swap(ch[cur][0],ch[cur][1]);
                tag[ch[cur][0]]^=1;
                tag[ch[cur][1]]^=1;
            }
        }
        int nex(){//找到后继
            push_down(rt);
            int x=ch[rt][1];
            while(1){
                push_down(x);
                if(!ch[x][0]){
                    break;
                }
                x=ch[x][0];
            }
            return x;
        }
        void rotate(int x){
            int y=fa[x];
            int z=fa[y];
            push_down(y);
            push_down(x);
            int chk=get(x);
            ch[y][chk]=ch[x][chk^1];
            if(ch[x][chk^1]){
                fa[ch[x][chk^1]]=y;
            }
            fa[y]=x;
            ch[x][chk^1]=y;
            fa[x]=z;
            if(z){
                ch[z][y==ch[z][1]]=x;
            }
            push_up(y);
            push_up(x);
        }
        void splay(int x,int y){
            for(int f;(f=fa[x])!=y;rotate(x)){
                if(fa[f]==y){
                    continue;
                }
                if(get(x)==get(f)){
                    rotate(f);
                }else{
                    rotate(x);
                }
            }
            if(y==0){
                rt=x;
            }
        }
        void reverse(int l,int r){
            splay(l,0);
            splay(r,l);
            tag[ch[r][0]]^=1;
        }
    }tree;//Splay模板
    bool cmp(object a,object b){
        if(a.val<b.val){
            return 1;
        }else{
            if(a.val==b.val){
                return a.num<b.num;
            }
        }
        return 0;
    }
    bool cmp2(object a,object b){
        return a.num<b.num;
    }
    signed main(){
        n=read();
        for(int i=1;i<=n;++i){
            ob[i+1].val=read();
            ob[i+1].num=i+1;
        }
        ob[1].val=0;
        ob[n+2].val=n+1;
        sort(ob+2,ob+n+2,cmp);
        for(int i=2;i<=n+1;i++){
            //离散
            ob[i].val=i-1;
        }
        sort(ob+2,ob+2+n,cmp2);
        rt=tree.build(1,n+2,0);//以类似线段树的方式建数
        for(int i=1;i<=n;++i){
            int x=p[i];//找到该权值对应的点的位置
            tree.splay(x,0);
            printf("%lld ",si[ch[x][0]]);
            x=tree.nex();//该权值的后继
            int y=p[i-1];//该权值的前驱
            tree.reverse(y,x);//区间翻转
        }
        return 0;
    }
    
    • 1

    信息

    ID
    3363
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者