BZOJ2329 HNOI2011 括号修复 平衡树

题意:给定一个由(,)组成的括号序列,维护:1、将[a,b]修改为同一种半括号  2、将[a,b]翻转  3、将[a,b]的(变为),)变为(  4、求[a,b]最少要添加多少个括号才能合法

题解:

不算太裸的平衡树……论标记的正确打法。

对于一个括号序列,我们总能简化成一个左边全是右括号,右边全是左括号的序列,像酱紫:)))))(((((。当然有可能是没有左括号或者右括号的

我们定义)==-1,(==1。然后我们用打标记的方法来维护从左起的最小序列和lmin和从右起的最大序列和rmax,显然这两个的值分别是简化后右括号和左括号的数量,那么答案就是(-lmin+1)/2+(rmax+1)/2

我们建树来维护lmin,lmax,rmin,rmax,翻转、改变、取反操作全部用打标记的方法,至于怎么打可以参考线段树的lazy标记,其中取反操作就是交换min和max。

有一个细节上的问题:如果改变操作在取反操作之后进行,就要删除取反标记,因为之前不论你怎么取反,一改变就统统作废了。下方标记时亦是如此。

最后就是代码实现了……

#include <cstdio>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN=100000+2;
typedef struct NODE{
    int c,s,v,lmin,rmin,lmax,rmax;
    bool same,rev,inv;
    NODE *child[2],*f;
    NODE(int _v,NODE *_f):s(_v),v(_v),f(_f){}
} *TREE;
TREE root,Null;
int N,M,a[MAXN];
char s[MAXN];

TREE NewNode(int v,TREE f){
    TREE x=new NODE(v,f);
    x->c=1;
    x->lmax=x->rmax=max(v,0);
    x->lmin=x->rmin=min(v,0);
    x->same=x->rev=x->inv=0;
    x->child[0]=x->child[1]=Null;
    return x;
}

void Initialization(){
    Null=NewNode(0,0),Null->c=0;
    root=NewNode(0,Null);
    root->child[1]=NewNode(0,root);
    Null->s=root->s=root->child[1]->s=0;
}

void Pushup(TREE x){
    if(x==Null) return;
    x->c=x->child[0]->c+x->child[1]->c+1;
    x->s=x->child[0]->s+x->child[1]->s+x->v;
    x->lmin=min(x->child[0]->lmin,x->child[0]->s+x->v+min(0,x->child[1]->lmin));
    x->rmin=min(x->child[1]->rmin,x->child[1]->s+x->v+min(0,x->child[0]->rmin));
    x->lmax=max(x->child[0]->lmax,x->child[0]->s+x->v+max(0,x->child[1]->lmax));
    x->rmax=max(x->child[1]->rmax,x->child[1]->s+x->v+max(0,x->child[0]->rmax));
}

void Pushdown(TREE x){
    if(x==Null) return;
    if(x->rev){
        swap(x->child[0],x->child[1]),swap(x->lmin,x->rmin),swap(x->lmax,x->rmax);
        x->child[0]->rev^=1,x->child[1]->rev^=1,x->rev=0;
    }
    if(x->same){
    x->s=x->v*x->c;
        x->lmax=x->rmax=max(0,max(x->v,x->s));
        x->lmin=x->rmin=min(0,min(x->v,x->s));
        x->child[0]->same=1,x->child[0]->v=x->v,x->child[0]->inv=0;//删除取反标记
        x->child[1]->same=1,x->child[1]->v=x->v,x->child[1]->inv=0;//删除取反标记
        x->same=0;
    }
    if(x->inv){
        x->v=-x->v,x->s=-x->s;
        swap(x->lmin,x->lmax),swap(x->rmin,x->rmax);
        x->lmin*=-1,x->rmin*=-1,x->lmax*=-1,x->rmax*=-1;
        x->child[0]->inv^=1,x->child[1]->inv^=1,x->inv=0;
    }
}

void Rotate(TREE x,bool t){
    TREE y=x->f;
    Pushdown(x->child[0]),Pushdown(x->child[1]),Pushdown(y->child[t]);

    y->child[!t]=x->child[t],x->child[t]->f=y,x->f=y->f;
    if(y->f->child[0]==y) y->f->child[0]=x;
    else y->f->child[1]=x;
    y->f=x,x->child[t]=y;

    Pushup(y),Pushup(x);
    if(y==root) root=x;
}

void Splay(TREE x,TREE y){
    Pushdown(x);
    while(x->f!=y)
        if(x->f->f==y)
            if(x->f->child[0]==x) Rotate(x,1);
            else Rotate(x,0);
        else if(x->f->f->child[0]==x->f)
            if(x->f->child[0]==x) Rotate(x->f,1),Rotate(x,1);
            else Rotate(x,0),Rotate(x,1);
        else
            if(x->f->child[0]==x) Rotate(x,1),Rotate(x,0);
            else Rotate(x->f,0),Rotate(x,0);
}

void Select(int p,TREE y){
    TREE x=root;Pushdown(x);
    while(p!=x->child[0]->c+1){
        if(p<=x->child[0]->c) x=x->child[0];
        else p-=x->child[0]->c+1,x=x->child[1];
        Pushdown(x);
    }
    Splay(x,y);
}

void Insert(int p,int n,int *a){
    TREE s,t;
    s=t=NewNode(a[1],Null);
    for(int i=2;i<=n;i++) t=t->child[1]=NewNode(a[i],t);

    Select(p+1,Null),Select(p+2,root);
    root->child[1]->child[0]=s,s->f=root->child[1];
    Splay(t,Null);
}

void Change(int p,int n,int v){
    Select(p,Null),Select(p+n+1,root);
    root->child[1]->child[0]->same=1,root->child[1]->child[0]->v=v;
    root->child[1]->child[0]->inv=0;//删除取反标记
    Splay(root->child[1]->child[0],Null);
}

void Reverse(int p,int n){
    Select(p,Null),Select(p+n+1,root);
    root->child[1]->child[0]->rev^=1;
    Splay(root->child[1]->child[0],Null);
}

void Invert(int p,int n){
    Select(p,Null),Select(p+n+1,root);
    root->child[1]->child[0]->inv^=1;
    Splay(root->child[1]->child[0],Null);
}

int Query(int p,int n){
    Select(p,Null),Select(p+n+1,root);
    Pushdown(root->child[1]->child[0]);
    int x=(-root->child[1]->child[0]->lmin+1)>>1;
    int y=(root->child[1]->child[0]->rmax+1)>>1;
    return x+y;
}

int main(){
    Initialization();

    cin >> N >> M;
    cin >> s;
    for(int i=1;i<=N;i++)
        if(s[i-1]==‘(‘) a[i]=1;
        else a[i]=-1;
    Insert(0,N,a);

    for(int i=1,a,b;i<=M;i++){
        cin >> s;
        cin >> a >> b;
        if(strstr(s,"Replace")){
            cin >> s;
            if(s[0]==‘(‘) Change(a,b-a+1,1);
            else Change(a,b-a+1,-1);
        }
        if(strstr(s,"Swap")) Reverse(a,b-a+1);
        if(strstr(s,"Invert")) Invert(a,b-a+1);
        if(strstr(s,"Query")) cout << Query(a,b-a+1) << endl;
    }

    return 0;
}

时间: 2024-08-04 03:38:10

BZOJ2329 HNOI2011 括号修复 平衡树的相关文章

bzoj千题计划222:bzoj2329: [HNOI2011]括号修复(fhq treap)

http://www.lydsy.com/JudgeOnline/problem.php?id=2329 需要改变的括号序列一定长这样 :)))((( 最少改变次数= 多余的‘)’/2 [上取整] + 多余的‘(’ /2 [上取整] 把 ‘)’ 看做1,‘(’ 看做-1 那么最少改变次数=最大前缀和/2 [上取整]+ 最小后缀和/2 [上取整] 覆盖标记的优先级高于翻转标记和取反标记 即下放覆盖标记时,同时清空翻转标记和取反标记 且先下放覆盖标记 翻转: 最大前缀和 和 最大后缀和 交换 最小前

bzoj2329: [HNOI2011]括号修复 Splay

神题. 用-1表示左括号,1表示右括号,lmax表示从左开始的最大连续和,rmin表示从右开始的最小连续和,答案为(lmax+1)/2+(-rmin+1)/2. Splay维护即可. #include<bits/stdc++.h> #define L(t) (t)->c[0] #define R(t) (t)->c[1] #define Z(t) (L(t)->s+1) #define M (l+r>>1) using namespace std; struct

BZOJ2329 [HNOI2011]括号修复

把左括号看做$1$,右括号看做$-1$,于是查询操作等于查询一个区间左边右边最大(最小)子段和 支持区间翻转,反转,覆盖操作...注意如果有覆盖操作,之前的操作全部作废了...于是在下传标记的时候要最后做... 1 /************************************************************** 2 Problem: 2329 3 User: rausen 4 Language: C++ 5 Result: Accepted 6 Time:4252

BZOJ 2329: [HNOI2011]括号修复( splay )

把括号序列后一定是))))((((这种形式的..所以维护一个最大前缀和l, 最大后缀和r就可以了..答案就是(l+1)/2+(r+1)/2...用splay维护,O(NlogN). 其实还是挺好写的, 只是我傻X -------------------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstrin

●BZOJ 2329 [HNOI2011]括号修复.cpp

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2329 题解: Splay 类似 BZOJ 2329 [HNOI2011]括号修复 只是多了一个Replace(替换)操作, 然后就要主要lazy标记之间的影响了. 1).Replace可以直接覆盖另外两个标记, 2).当已经有Replace标记,再覆盖Invert标记时,直接把Replace标记取反即可;在覆盖Swap标记时,Replace标记不变. 代码: #include<cstdio

BZOJ 2329: [HNOI2011]括号修复 [splay 括号]

题目描述 一个合法的括号序列是这样定义的: 空串是合法的. 如果字符串 S 是合法的,则(S)也是合法的. 如果字符串 A 和 B 是合法的,则 AB 也是合法的. 现在给你一个长度为 N 的由‘('和‘)'组成的字符串,位置标号从 1 到 N.对这个字符串有下列四种操作: Replace a b c:将[a,b]之间的所有括号改成 c.例如:假设原来的字符串为:))())())(,那么执行操作 Replace 2 7 ( 后原来的字符串变为:)(((((()(. Swap a b:将[a,b]

【BZOJ】2329: [HNOI2011]括号修复(splay+特殊的技巧)

http://www.lydsy.com/JudgeOnline/problem.php?id=2329 和前一题一样,不就多了个replace操作吗.好,就打一下. 然后交上去wa了.................... 看了题解,好神奇! 记住:以后pushdown的tag要考虑先后顺序! 因为invert和swap操作谁先谁后没有关系,那么考虑invert和replace这两个有冲突的关系 为什么有冲突呢?因为假如你replace的标记在先,invert标记在后,但是invert在pus

2329: [HNOI2011]括号修复

传送魔法 一开始以为可以直接线段树的,好像还是不行--还是得用Spaly,然后就没啥了. #include<cstdio> #include<algorithm> #define MN 210000 using namespace std; inline int read(){ int ca=getchar(),p=0; while (ca<'0'||ca>'9') ca=getchar(); while (ca>='0'&&ca<='9')

[HNOI2011][bzoj 2329] 括号修复 [splay+前缀和]

题面: http://www.lydsy.com/JudgeOnline/problem.php?id=2329 思路: 显然,操作4中输出补全的最小费用是关键 这决定了我们不可能在splay上只维护1-2个值. 考虑一段括号序列,将其中所有合法序列删去以后,留下的一定是形如 ))))))((( 的序列 因此首先考虑将每段区间左侧不匹配的括号数和右侧不匹配的括号数记录下来,分别为 left[l,r] 和 right[l,r] 此时除了Invert操作以外已经可以满足 但是对于Invert操作,对