[BZOJ 3223] 文艺平衡树

Link:

BZOJ 3223 传送门

Solution:

$Splay$对序列操作的模板题

核心思想就是将$L-1$移到根,$R+1$移至$L-1$的下方,从而约束出区间$[L,R]$进行操作

对于此题仅要求将区间翻转,因此只要将左右子树交换即可

但同时类似于线段树要打上懒惰标记来保证复杂度,每次查询第$K$大时下放标记

 

Tips:

1、对于此题不再关心每个数的权值,而只关心其在序列中的编号

而这个节点的排名($sz[ch[x][0]]+1$)就是其当前的序号

2、为了处理$[1,n]$等边界情况,新增$1,n+2$作为真·边界

3、一开始可以通过分治直接建树,不用一个个$insert$,但要将$f[rt]$置为$0$!!!

Code:

#include <bits/stdc++.h>

using namespace std;
const int MAXN=1e5+10;
int n,m,rt,l,r,sz[MAXN],f[MAXN],ch[MAXN][2],tag[MAXN];

void pushup(int x)
{sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;}

void pushdown(int x)
{
    if(!tag[x]) return;
    tag[ch[x][1]]^=1;tag[ch[x][0]]^=1;
    swap(ch[x][0],ch[x][1]);tag[x]=0;
}

void Build(int l,int r,int anc)
{
    if(l>r) return;
    int mid=(l+r)/2;
    if(mid<anc) ch[anc][0]=mid;
    else ch[anc][1]=mid;
    sz[mid]=1;f[mid]=anc;
    if(l==r) return;
    
    Build(l,mid-1,mid);Build(mid+1,r,mid);
    pushup(mid);
}

void Print(int x)
{
    pushdown(x);
    if(ch[x][0]) Print(ch[x][0]);
    if(x>1&&x<n+2) printf("%d ",x-1);
    if(ch[x][1]) Print(ch[x][1]);
}

void Rotate(int x)
{
    int y=f[x],z=f[y],k=(ch[y][1]==x);
    ch[z][ch[z][1]==y]=x;f[x]=z;
    ch[y][k]=ch[x][k^1];f[ch[x][k^1]]=y;
    ch[x][k^1]=y;f[y]=x;
    pushup(x);pushup(y);
}

void Splay(int x,int up)
{
    while(f[x]!=up)
    {
        int y=f[x],z=f[y];
        if(z!=up) (ch[y][1]==x)^(ch[z][1]==y)?Rotate(x):Rotate(y);
        Rotate(x);
    }
    if(!up) rt=x;
}

int Kth(int x)
{
    int k=rt;
    while(true)
    {
        pushdown(k);
        if(x==sz[ch[k][0]]+1) return k;
        else if(sz[ch[k][0]]>=x) k=ch[k][0];
        else x-=sz[ch[k][0]]+1,k=ch[k][1];
    }
}

void Reverse(int l,int r)
{
    int x=Kth(l),y=Kth(r+2);
    Splay(x,0);Splay(y,x);
    tag[ch[ch[rt][1]][0]]^=1;
}

int main()
{
    scanf("%d%d",&n,&m);
    rt=(n+3)/2;Build(1,n+2,rt);f[rt]=0;
    
    for(int i=1;i<=m;i++)
        scanf("%d%d",&l,&r),Reverse(l,r);
    Print(rt);
    return 0;
}

 

posted @ 2018-07-19 20:28  NewErA  阅读(124)  评论(0编辑  收藏  举报