Gym 100803G Flipping Parentheses

题意:就是给你一个匹配好的括号串长度为n,在给你m个操作,每个操作就给翻转摸一个位置的括号,对于每一个操作,你要找到一括号将其翻转,使得最后这个串还是匹配的,若存在多个解,翻转最左边的那个括号。


思路:如果操作是翻转有某个括号,那么我们一定的翻转与之相反的而一个括号。

把左括号转化为1,有括号转化为-1,用线段树来维护前缀和;
因为若一个完全匹配的括号串,他的每一个位置的前缀和一定是大于等于0的;
当操作将在i位置右括号翻转,那么i位置以后的所有前缀和都加2;
我们要是的这个达到平衡,于是就去寻找一个左括号翻转,使得其后面的前缀和都减二,最终达到平衡。
对于这个左括号怎么找?我们可用二分,因为操作之影响i位置以后的前缀和,i之前的是不会改变的;
因此我们只要找到一段区间,这段区间内每一个数都大于等于二。

当操作将左括号翻转的时候,和上面的思路一样,但是这个时候你只要在这个i位置之前找一个最左边的左括号j就可以了;
为什么?因为把这个左括号j翻转(j在i前面),j之后的都加上2,这样是不是保证后面一定是每一个前缀和都是大于等于0的。
于是我们要去寻找一个最左边的括号。这个相对比较简单,可以直接用set来维护。


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#pragma comment(linker, "/STACK:1024000000,1024000000")
template 
bool scanff(T &ret){
    char c; int sgn; T bit=0.1;
    if(c=getchar(),c==EOF) return 0;
    while(c!='-'&&c!='.'&&(c<'0'||c>'9')) c=getchar();
    sgn=(c=='-')?-1:1;
    ret=(c=='-')?0:(c-'0');
    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    if(c==' '||c=='\n'){ ret*=sgn; return 1; }
    while(c=getchar(),c>='0'&&c<='9') ret+=(c-'0')*bit,bit/=10;
    ret*=sgn;
    return 1;
}
#define inf 1073741824
#define llinf 4611686018285162540LL
#define eps 1e-8
#define mod 9223372034707292160LL
#define pi acos(-1.0)
#define lth (th<<1)
#define rth (th<<1|1)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define drep(i,a,b) for(int i=a;i>=b;i--)
#define mset(x,val) memset(x,val,sizeof(x))
#define mcpy(x,y) memcpy(x,y,sizeof(y))
#define findx(x) lower_bound(b+1,b+1+bn,x)-b
#define mpii(a,b) make_pair(a,b);
#define NN 101010
#define MM 202020
using namespace std;
typedef long long ll;
typedef long double lb;
typedef pair pii;
struct node{
    int l,r,val,add;
}t[NN*20];
int n,m,h;
int a[NN*10],b[NN*10];
char s[NN*10];
int ql,qr,qv,qmi;
setst;

void built(){
    for(h=1;hl){
        t[th].val=min(t[lth].val,t[rth].val);
        t[th].val+=t[th].add;
    }
    else{
        t[th].val+=t[th].add;
        t[th].add=0;
    }
}

void update(int th,int l,int r){
    if(ql<=l&&qr>=r){
        t[th].add+=qv;
    }
    else{
        int mid=(r+l)>>1;
        if(ql<=mid) update(lth,l,mid);
        if(qr>mid)  update(rth,mid+1,r);
    }
    maintain(th,l,r);
}

void query(int th,int l,int r,int add){
    if(ql<=l&&qr>=r){
        qmi=min(qmi,t[th].val+add);
    }
    else{
        int mid=(r+l)>>1;
        if(ql<=mid) query(lth,l,mid,t[th].add+add);
        if(qr>mid)  query(rth,mid+1,r,t[th].add+add);
    }
}

void add(int pos){
    a[pos]=-1;
    st.insert(pos);
    ql=pos;
    qr=n;
    qv=-2;
    update(1,0,h-1);
}

void dlt(int pos){
    a[pos]=1;
    st.erase(pos);
    ql=pos;
    qr=n;
    qv=2;
    update(1,0,h-1);
}

int main(){
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    b[0]=0;
    rep(i,1,n){
        if(s[i]=='(') a[i]=1;
        if(s[i]==')') a[i]=-1,st.insert(i);
        b[i]=b[i-1]+a[i];
    }
    built();
    int ans;
    rep(i,1,m){
        int pos;
        scanf("%d",&pos);
        if(a[pos]==1){
            add(pos);
            ans=*st.begin();
            dlt(ans);
        }
        else{
            dlt(pos);
            int l,r;
            l=1,r=n;
            while(l<=r){
                ql=(l+r)>>1;
                qr=n;
                qmi=inf;
                query(1,0,h-1,t[1].add);
                if(qmi>=2) r=ql-1;
                else l=ql+1;
            }
            ans=l;
            add(ans);
        }
        printf("%d\n",ans);
    }
    return 0;
}



你可能感兴趣的:(线段树)