Luogu-3391 (splay模板)

题目

文艺平衡树
https://www.luogu.org/problemnew/show/P3391
此程序还可以提交 BZOJ-3223

分析

  • 第一次写splay,程序里有双旋和单旋两种,不过luogu上时间好像差别不大……(甚至好像双旋还更慢?)
  • 本来写旋转的时候没用到 & 这一点,后来看到某篇博客上面,才发现这个技巧,可以有效地减少代码量。
  • 弄个 tag 说明此字树需要翻转,下传的时候把其左右儿子交换即可。
  • 要是有 dalao 可以帮我修改一下,那也谢谢了!(看了一下其他人的时间,好像比我的快好多……)

程序

#include <cstdio>
#include <algorithm>
using namespace std;
int fa[100005],ls[100005],rs[100005],sz[100005],tag[100005],root,n,m;
void mer(int x){sz[x]=sz[ls[x]]+sz[rs[x]]+1;}

void bui(int l,int r,int Fa){   //建立一棵相对平衡的二叉树 
    if (l>r) return;
    int mid=l+r>>1;
    fa[mid]=Fa;
    if (mid>Fa) rs[Fa]=mid;else ls[Fa]=mid;
    bui(l,mid-1,mid); bui(mid+1,r,mid);
    mer(mid);
}

void node_up(int x){    //在不改变中序的前提下把节点 x 往上弄一层 
    int Fa=fa[x],L=ls[x],R=rs[x];
    if (Fa==ls[fa[Fa]]) ls[fa[Fa]]=x;else rs[fa[Fa]]=x;
    if (x==ls[Fa]){
        fa[x]=fa[Fa],fa[Fa]=x,fa[R]=Fa;
        rs[x]=Fa,ls[Fa]=R;
    }else{
        fa[x]=fa[Fa],fa[Fa]=x,fa[L]=Fa;
        ls[x]=Fa,rs[Fa]=L;
    }
    mer(x); mer(Fa);
    if (fa[x]==0) root=x;
}

void tag_add(int x){tag[x]^=1;}
void tag_down(int x){
    if (tag[x]){
        swap(ls[x],rs[x]);
        tag[ls[x]]^=1;
        tag[rs[x]]^=1;
        tag[x]=0;
    }
}

int find(int k){        //找到当前序列中第 k 个数 
    //由于多设了一个 0 节点,所以只需 cnt==k (中序遍历中左边有 k 个数)就是实际序列中第 k 个数 
    for (int x=root,cnt=-1; cnt!=k; ){
        tag_down(x);
        if (x==ls[fa[x]]) cnt-=sz[rs[x]]+1;
        else cnt+=sz[ls[x]]+1;
        if (cnt==k) return x;
        x=(cnt<k) ? rs[x]:ls[x];
    }
}

void To_(int x,int &goal){  //单旋 
    if (x==goal) return;
    while (goal!=x) node_up(x);
}
void To(int x,int &goal){   //双旋 
    while (goal!=x){
        if (fa[x]==goal) {node_up(x); break;}
        node_up(fa[x]);
        node_up(x);
    }
}

void dfs(int x){
    if (!x) return;
    tag_down(x);
    dfs(ls[x]); if (x>1 && x<=n+1) printf("%d ",x-1); dfs(rs[x]);
}

int main(){
    //freopen("1.txt","r",stdin);
    scanf("%d%d",&n,&m);
    root=n+3>>1; bui(1,n+2,0);      //加入节点 0 和 n+1,然后为了代码方便,把每个节点的键值加一 
    for (int l,r,L,R; m--; ){
        scanf("%d%d",&l,&r);
        L=find(l-1); To(L,root);    //把 (l-1) 弄到根节点上 
        R=find(r+1); To(R,rs[L]);   //把 (r+1) 弄到根节点的右儿子上 
        tag_add(ls[R]);             //打上翻转标记 
    }
    dfs(root);      //输出 
}

猜你喜欢

转载自blog.csdn.net/jackypigpig/article/details/78643730