BZOJ 2209 括号序列(splay)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2209

题意:给出一个括号序列,仅包含(和)。三种操作:(1)将[L,R]区间内的括号反转,即(变为),)变为(;(2)将[L,R]区间内的括号翻转,之前为a[L],a[L+1]……a[R-1],a[R],现在为a[R],a[R-1],……a[L+1],a[L];(3)询问[L,R]内至少需要修改多少个括号才能使得两两匹配?这里貌似可以认为这个询问的区间长度为偶数。

思路:对于一段括号,最后未匹配的必然是))))((((这种形式,或者是只剩下一种括号。假如现在知道最后未匹配的右括号为x个,左括号为y个,那么最少修改个数为:

即可以将右括号的前一半修改为左括号,左括号的后一半修改为右括号;若x和y为奇数,则各剩下一个,这两个剩下的均需要修改。为了比较容易维护,我们将左括号看做1,右括号看做-1,splay的每个节点保存LMin(左端连续最小和),LMax,RMin,RMax,这样剩余的右括号的个数显然就是-LMin,剩余的左括号的个数就是RMax,那么答案就是:

注意,这两种形式看起来是等价的,但是后一种才是真正正确的,即包含了所有情况。因为在这里我们会觉得在大多数时候LMin是个负数。但是有时候可能是正的,比如剩余括号序列为((((,此时LMin=1,RMax=4,答案为2,即将后两个修改为右括号。

struct node
{
    int op1,op2,val,LMin,LMax,RMin,RMax,sum;
    int size;
    node *c[2],*p;


    void flip()
    {
        val*=-1; sum*=-1;
        int temp;
        temp=LMin; LMin=-LMax; LMax=-temp;
        temp=RMin; RMin=-RMax; RMax=-temp;
        op1^=1;
    }


    void turnover()
    {
        swap(LMin,RMin);
        swap(LMax,RMax);
        swap(c[0],c[1]);
        op2^=1;
    }
};


node a[N],*root,*nullNode;
int cnt;


void pushUp(node *p)
{
    if(p==nullNode) return;
    p->size=1+p->c[0]->size+p->c[1]->size;
    p->sum=p->c[0]->sum+p->val+p->c[1]->sum;


    p->LMin=p->c[0]->LMin;
    p->LMin=min(p->LMin,p->c[0]->sum+p->val);
    p->LMin=min(p->LMin,p->c[0]->sum+p->val+p->c[1]->LMin);


    p->LMax=p->c[0]->LMax;
    p->LMax=max(p->LMax,p->c[0]->sum+p->val);
    p->LMax=max(p->LMax,p->c[0]->sum+p->val+p->c[1]->LMax);


    p->RMin=p->c[1]->RMin;
    p->RMin=min(p->RMin,p->c[1]->sum+p->val);
    p->RMin=min(p->RMin,p->c[1]->sum+p->val+p->c[0]->RMin);


    p->RMax=p->c[1]->RMax;
    p->RMax=max(p->RMax,p->c[1]->sum+p->val);
    p->RMax=max(p->RMax,p->c[1]->sum+p->val+p->c[0]->RMax);
}


int ok(node *p)
{
    return p!=nullNode&&(p!=&a[1])&&(p!=&a[2]);
}


void pushDown(node *p)
{
    if(p==nullNode) return;
    if(p->op1)
    {
        if(ok(p->c[0])) p->c[0]->flip();
        if(ok(p->c[1])) p->c[1]->flip();
        p->op1=0;
    }
    if(p->op2)
    {
        if(ok(p->c[0])) p->c[0]->turnover();
        if(ok(p->c[1])) p->c[1]->turnover();
        p->op2=0;
    }
}


node *newNode(int val,node *p)
{
    node *e=&a[cnt++];
    e->c[0]=e->c[1]=nullNode;
    e->p=p;
    e->op1=e->op2=0;
    e->size=1;
    e->sum=e->val=e->LMin=e->RMin=e->LMax=e->RMax=val;
    return e;
}


void init()
{
    nullNode=0;
    nullNode=newNode(0,nullNode);
    nullNode->size=0;
    nullNode->LMin=nullNode->RMin=INF;
    nullNode->LMax=nullNode->RMax=-INF;
    nullNode->sum=0;


    root=newNode(0,nullNode);
    root->LMin=root->RMin=INF;
    root->LMax=root->RMax=-INF;
    root->sum=0;
    root->c[1]=newNode(0,root);
    root->c[1]->LMin=root->c[1]->RMin=INF;
    root->c[1]->LMax=root->c[1]->RMax=-INF;
    root->c[1]->sum=0;
    pushUp(root);
}




void zig(node *x)
{
    node *p=x->p,*q=p->p;
    p->c[0]=x->c[1];
    if(x->c[1]!=nullNode) x->c[1]->p=p;
    x->c[1]=p;
    p->p=x;
    x->p=q;
    if(q!=nullNode)
    {
        if(q->c[0]==p) q->c[0]=x;
        else q->c[1]=x;
    }
    pushUp(p);
    pushUp(x);
    if(root==p) root=x;
}


void zag(node *x)
{
    node *p=x->p,*q=p->p;
    p->c[1]=x->c[0];
    if(x->c[0]!=nullNode) x->c[0]->p=p;
    x->c[0]=p;
    p->p=x;
    x->p=q;
    if(q!=nullNode)
    {
        if(q->c[0]==p) q->c[0]=x;
        else q->c[1]=x;
    }
    pushUp(p);
    pushUp(x);
    if(root==p) root=x;
}


void splay(node *x,node *goal)
{
    while(x->p!=goal)
    {
        if(x->p->p!=goal)
        {
            pushDown(x->p->p);
            pushDown(x->p);
            pushDown(x);
            if(x->p->p->c[0]==x->p)
            {
                if(x->p->c[0]==x) zig(x->p),zig(x);
                else zag(x),zig(x);
            }
            else
            {
                if(x->p->c[1]==x) zag(x->p),zag(x);
                else zig(x),zag(x);
            }
        }
        else
        {
            pushDown(x->p);
            pushDown(x);
            if(x->p->c[0]==x) zig(x);
            else zag(x);
        }
    }
}


void select(int k,node *goal)
{
    node *p=root;
    pushDown(p);
    while(k!=p->c[0]->size+1)
    {
        if(k<=p->c[0]->size) p=p->c[0];
        else
        {
            k-=1+p->c[0]->size;
            p=p->c[1];
        }
        pushDown(p);
    }
    splay(p,goal);
}


node *build(int L,int R,char str[],node *p)
{
    if(L>R) return nullNode;
    int mid=(L+R)>>1;
    node *e=newNode(str[mid]=='('?1:-1,p);
    e->c[0]=build(L,mid-1,str,e);
    e->c[1]=build(mid+1,R,str,e);
    pushUp(e);
    return e;
}


void insert(int p,int n,char str[])
{
    select(p+1,nullNode);
    select(p+2,root);
    node *e=build(1,n,str,root->c[1]);
    root->c[1]->c[0]=e;
    splay(e,nullNode);
}


int query(int L,int R)
{
    select(L,nullNode);
    select(R+2,root);
    node *p=root->c[1]->c[0];
    return -(p->LMin-1)/2+(p->RMax+1)/2;
}


void flip(int L,int R)
{
    select(L,nullNode);
    select(R+2,root);
    node *p=root->c[1]->c[0];
    p->flip();
    splay(p,nullNode);
}


void turnover(int L,int R)
{
    select(L,nullNode);
    select(R+2,root);
    node *p=root->c[1]->c[0];
    p->turnover();
    splay(p,nullNode);
}


int n,m;
char str[N];


int main()
{
    init(); RD(n,m); RD(str+1); insert(0,n,str);
    int op,x,y;
    while(m--)
    {
        RD(op,x,y);


        if(op==0) PR(query(x,y));
        else if(op==1) flip(x,y);
        else turnover(x,y);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值