[BZOJ2329][HNOI2011]括号修复-splay

括号修复

Description

pic1


作为当年负责讲解splay的本蒟蒻突然发现自己不记得splay怎么打了……
在经历了一番激烈的思想斗争后决定回来复习一下。
(因为咱突然发现6个多月过去了咱还从来没有用过平衡树系列的东西)
然后就被此题玩了个半死……

(╯‵□′)╯︵┻━┻


思路:
那些操作一看就是在钦定splay了……
然后考虑如何兹瓷询问。
假设现在有一个奇怪的括号序列:
))()()(((()))(()
咱来配个对:
))((
那么这就是咱要的答案:2/2+2/2=2。
也就是(左括号数+1)/2+(右括号数+1)/2(加一即向上取整)

如何维护消去后的左括号数和右括号数?
令’(‘为1,’)’为-1
那么可以发现,左括号数值就是前缀和的最小值,右括号数就是后缀和的最大值。
也就是左起最小和右起最大。

考虑到几种操作,或许咱们需要维护左起最大最小、右起最大最小四个tag?
理论上是的,但其实只需要左起最大最小,一共两个tag就够了……

考虑swap,交换左起最大和右起最小,右起最大和左起最小。
只维护两个的话,左起最大会变成区间和减去原左起最小,左起最小同理。
考虑invert,交换左起最大和左起最小,右起最大和右起最小。
只维护两个的话,同时乘以-1并交换位置就好。
考虑Replace,咱认为这不需要考虑。
考虑Query,可以发现右起最大加上左起最小的值等于区间和。
然后就没有然后了……

等等你说许多题解提到了一个tag的更新顺序?
事实上只需要在下传tag时优先Replace,并在更新时顺手清空子树的Invert标记即可~
因为在拥有了Replace标记后,子树的Invert标记被无效化了,此时咱们再下传当前节点的有效的Invert标记,就可以不打乱顺序了~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

const int N=1000009;

int n,q,root;
char s[N];
int ch[N][2],fa[N],val[N],siz[N],sum[N],tot;
int up[N],down[N],rev[N],inv[N],rep[N];

inline int maxx(int a,int b){if(a>b)return a;return b;}
inline int minn(int a,int b){if(a<b)return a;return b;}

inline void update(int x)
{
    siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
    sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];
    up[x]=maxx(up[ch[x][0]],up[ch[x][1]]+sum[ch[x][0]]+val[x]);
    down[x]=minn(down[ch[x][0]],down[ch[x][1]]+sum[ch[x][0]]+val[x]);
}

inline void invert(int x)
{
    inv[x]^=1;
    val[x]=-val[x];
    sum[x]=-sum[x];
    down[x]=-down[x];
    up[x]=-up[x];
    swap(up[x],down[x]);
}

inline void replace(int x,int ty)
{
    inv[x]=0;
    rep[x]=ty;
    if(ty==1)
    {
        val[x]=1;sum[x]=siz[x];
        up[x]=siz[x];down[x]=0;
    }
    else
    {
        val[x]=-1;sum[x]=-siz[x];
        up[x]=0;down[x]=-siz[x];
    }
}

inline void reverse(int x)
{
    rev[x]^=1;
    up[x]=sum[x]-up[x];
    down[x]=sum[x]-down[x];
    swap(up[x],down[x]);
}

inline void push(int x)
{
    if(rev[x])
    {
        swap(ch[x][0],ch[x][1]);
        reverse(ch[x][0]);
        reverse(ch[x][1]);
        rev[x]=0;
    }
    if(rep[x])
    {
        replace(ch[x][0],rep[x]);
        replace(ch[x][1],rep[x]);
        rep[x]=0;
    }
    if(inv[x])
    {
        invert(ch[x][0]);
        invert(ch[x][1]);
        inv[x]=0;
    }
}

inline void prepush(int x)
{
    if(fa[x])
        prepush(fa[x]);
    push(x);
}

inline int find(int x,int k)
{
    push(x);
    if(k==siz[ch[x][0]]+1)
        return x;
    else if(k<=siz[ch[x][0]])
        return find(ch[x][0],k);
    else
        return find(ch[x][1],k-siz[ch[x][0]]-1);
}

inline void rotate(int x)
{
    int y=fa[x],z=fa[y];
    int l=(ch[y][1]==x);
    if(z)ch[z][ch[z][1]==y]=x;
    fa[x]=z;fa[y]=x;fa[ch[x][l^1]]=y;
    ch[y][l]=ch[x][l^1];ch[x][l^1]=y;
    update(y);update(x);
}

inline int build(int l,int r)
{
    if(l>r)return 0;
    int mid=l+r>>1,now=++tot;
    val[now]=(s[mid]=='('?1:-1);
    ch[now][0]=build(l,mid-1);
    ch[now][1]=build(mid+1,r);
    fa[ch[now][0]]=fa[ch[now][1]]=now;
    update(now);
    return now;
}

inline void splay(int x,int target=0)
{
    prepush(x);
    while(fa[x]!=target)
    {
        int y=fa[x],z=fa[y];
        if(z!=target)
        {
            if((ch[y][0]==x)^(ch[z][0]==y))rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
    if(!target)
        root=x;
    update(x);
}

int main()
{
    scanf("%d%d",&n,&q);
    scanf("%s",s+1);
    root=build(0,n+1);

    for(int i=1,l,r;i<=q;i++)
    {
        scanf("%s%d%d",s+1,&l,&r);
        l=find(root,l);
        r=find(root,r+2);
        splay(l);
        splay(r,root);
        if(s[1]=='R')
        {
            scanf("%s",s+1);
            replace(ch[r][0],s[1]=='('?1:-1);
        }
        else if(s[1]=='S')
            reverse(ch[r][0]);
        else if(s[1]=='I')
            invert(ch[r][0]);
        else
            printf("%d\n",(-down[ch[r][0]]+1)/2+(sum[ch[r][0]]+(-down[ch[r][0]]+1)/2*2)/2);
        update(l);
        update(r);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值