splay文艺平衡树

模板题传送

就是简单的区间翻转操作,打标记就好。代码易懂

#include<bits/stdc++.h>
#define INF 2100000001
#define N 100003
using namespace std;
int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
int root=0,ndnum=0;
int fa[N],ch[N][4],key[N],tag[N],siz[N],data[N];
int build(int f,int l,int r)//与线段树的build是有一些区别的 
{
    if(l>r) return 0;//即为空节点
    int mid=(l+r)>>1;
    int now=++ndnum;
    key[now]=data[mid],fa[now]=f,tag[now]=0;//tag标记 
    ch[now][0]=build(now,l,mid-1);
    ch[now][1]=build(now,mid+1,r);
    siz[now]=siz[ch[now][0]]+siz[ch[now][1]]+1;
    return now;
}
int get(int x)
{
    return ch[fa[x]][1]==x;
}
void update(int x)
{
    siz[x]=siz[ch[x][1]]+siz[ch[x][0]]+1;
}
void pushdown(int x)
{
    if(x&&tag[x])
    {
        tag[ch[x][0]]^=1;
        tag[ch[x][1]]^=1;
        swap(ch[x][1],ch[x][0]);
        tag[x]=0;
    }
}
void rotate(int x)
{
    int old=fa[x],oldf=fa[old];
    int which=get(x);
    ch[old][which]=ch[x][which^1];
    fa[ch[old][which]]=old;
    fa[old]=x;ch[x][which^1]=old;
    fa[x]=oldf;
    if(oldf) ch[oldf][ch[oldf][1]==old]=x;
    update(old);update(x);
}
void splay(int x,int tar)
{
    for(int f;(f=fa[x])!=tar;rotate(x))
      if(fa[fa[x]]!=tar) rotate((get(x)==get(f))?f:x);
    if(!tar) root=x;
}
int rank(int x)
{
    int now=root;
    while(1)
    {
        pushdown(now);
        if(x<=siz[ch[now][0]]) now=ch[now][0];
        else
        {
            x-=siz[ch[now][0]]+1;
            if(!x)return now;
            now=ch[now][1];
        }
    }
}
void turn(int l,int r)
{
    int x=rank(l);
    int y=rank(r+2);
    
    splay(x,0);splay(y,x);
    pushdown(root);
    tag[ch[ch[root][1]][0]]^=1;
}
void dfs(int x)
{
    pushdown(x);
    if(ch[x][0])dfs(ch[x][0]);
    if(key[x]!=-INF&&key[x]!=INF) printf("%d ",key[x]);
    if(ch[x][1])dfs(ch[x][1]);
}
int main()
{
    int n=read(),m=read();
    for(int i=1;i<=n;++i) data[i+1]=i;
    data[1]=-INF;data[n+2]=INF;//在反转区间[l~r]的时候,我们可以考虑利用Splay的性质,
    //将l-1翻转至根节点,再将r+1翻转至根节点的幼儿子为了方便,在1号节点之前n号节点之后加两个节点赋值为-INF和INF
    //作为虚点,既满足二叉搜索树的性质,又可以让我们在翻转1~n时不会GG
    root=build(0,1,n+2);
    for(int i=1;i<=m;++i)
    {
        int x=read(),y=read();
        turn(x,y);
    }
    dfs(root);
}
View Code

顺便提一嘴的是,关于翻转标记下传时,如果还涉及了其他标记,要考虑影响(见具体题目,比如说还存了最大前缀和最大后缀,那么不仅左右儿子要交换,最大前缀和最大后缀也要进行交换)。

然后我们会发现这道题下标就是对应的值,然而有一些题下标并不是值域,存入key就好了,只是哨兵节点的值可能要变化一下(根据不同题目)。

原文地址:https://www.cnblogs.com/yyys-/p/11240425.html