uestc 1546 Bracket Sequence(线段树 括号匹配)

Bracket Sequence

Time Limit: 3000 ms Memory Limit: 65536 kB Solved: 28 Tried: 380

Description

There is a sequence of brackets, which supports two kinds of operations.
1. we can choose a interval [l,r], and set all the elements range in this interval to left bracket or right bracket. 
2. we can reverse a interval, which means that for all the elements range in [l,r], if it's left bracket at that time, we change it into right bracket, vice versa.
Fish is fond of "Regular Bracket Sequence", so he want to know whether a interval [l,r] of the sequence is regular or not after doing some opearations.

Let us define a regular brackets sequence in the following way: 

1. Empty sequence is a regular sequence. 
2. If S is a regular sequence, then (S) is also a regular sequences. 
3. If A and B are regular sequences, then AB is a regular sequence.

Input

In the first line there is an integer T (T≤10), indicates the number of test cases. Each case begins with a line containing an integers N (N ≤ 100,000 and N is a even number), the size of the initial brackets sequence. The next line contains a string whose length is N consisting of '(' and ')'. In the third of each test case, there is an integer M(M ≤ 100,000) indicates the number of queries. Each of the following M lines contains one operation as mentioned below. The index of the bracket sequence is labeled from 0 to N - 1.

Three operation description:
set l r c: change all the elements range in [l,r] into '(' or ')'.(c is '(' or ')')
reverse l r: reverse the interval [l,r]
query l,r: you should answer that interval [l,r] is regular or not

Output

For each test case, print a line containing the test case number (beginning with 1) on its own line, then the answer for each "query" operation, if the interval is regular, print "YES", otherwise print "NO", one on each line.
Print a blank line after each test case.

Simple Input

1
6
((()))
8
query 0 5
set 0 5 (
query 0 5
reverse 3 5
query 0 5
query 1 4
query 2 3
query 0 4

Simple Output

Case 1:
YES
NO
YES
YES
YES
NO

Hint

Huge input, use "scanf" instead of "cin".

Source

Classic Problem


<br/>

题目:http://acm.uestc.edu.cn/problem.php?pid=1546

题意:给你一串左右括号,每一次选一个区间的括号,或者全部变成左括号,或者变成右括号,或者取反,也就是左括号变成右括号。。。,也有可能是一个询问,问这个区间里的括号是不是刚好完全匹配。。。

分析:这题一开始没什么想法,主要是想成可能出现永远不能匹配的情况,其实那是多种括号了,后来想到了一种,保存匹配剩下的左括号,和匹配剩下的右括号,这个很明显,如果两种括号都剩下,那么剩下的左括号必定在右边,右括号在左边,而如果只剩下一种,那就没必要在意它在区间的左边还是右边了,也就是只要在乎一段区间剩余未匹配的括号的情况,如果都剩余0,就是完全匹配了,而对于一段区间如何更新呢?剩余的'('=左子树'('-min{ 左子树 '(' ,  右子树 ')'  }+右子树'('  ;剩余的')'=右子树')'-min{ 左子树 '(' ,  右子树 ')'  }+右子树')'  ;这样就可以算出整个区间的情况了,而对于取反的话,只要把左括号当做右括号统计一下,需要取反时把两个统计情况取反就行,而对于一个区间的set或rev,要先操作,再做标志,如果是set的话,一定要把rev标志清零(这个不小心没做,wa了一天。。。)

以上是我最初的想法,当然,这样做绝对是正解,因为见到有人这么做了= =

后来我觉得上面的写法可能很麻烦,然后就用别人的一种写法,把左括号当做-1,右括号当做1,对于一个区间,只要从左到右的和的最大值为0,并且区间的和为0,则这个区间就是完全匹配的,这个就不证明了。。。更新时区间的和就不用说了,而区间从左到右的最值,这个得从两个来取   最值=max(左子树最值,左子树和+右子树最值)

取反的话,区间和直接取相反数,最值的话,每次都保存最大和最小值,然后最大值=最小值的相反数,最小值同。。。。延迟标志的注意点同上。。。

最后发现这个写法貌似更不好写,而且实现起来的复杂度貌似高一点,可能是我写挫了= =,第一种就不写了,泪奔~~~

代码:

#include<cstdio>
#include<iostream>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int mm=111111;
int dly[mm<<2],rev[mm<<2],sum[mm<<2],lmax[mm<<2],lmin[mm<<2];
char str[mm];
void reverse(int rt)
{
    sum[rt]=-sum[rt];
    int tmp=lmax[rt];
    lmax[rt]=-lmin[rt];
    lmin[rt]=-tmp;
    rev[rt]^=1;
}
void set(int rt,int op,int len)
{
    sum[rt]=op*len;
    lmax[rt]=sum[rt]>0?sum[rt]:0;
    lmin[rt]=sum[rt]<0?sum[rt]:0;
    dly[rt]=op,rev[rt]=0;
}
int pushdown(int rt,int l1,int l2)
{
    if(rev[rt])
    {
        if(dly[rt])dly[rt]=-dly[rt];
        else reverse(rt<<1),reverse(rt<<1|1);
        rev[rt]=0;
    }
    if(dly[rt])
    {
        set(rt<<1,dly[rt],l1),set(rt<<1|1,dly[rt],l2);
        dly[rt]=0;
    }
}
void pushup(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    lmax[rt]=max(lmax[rt<<1],sum[rt<<1]+lmax[rt<<1|1]);
    lmin[rt]=min(lmin[rt<<1],sum[rt<<1]+lmin[rt<<1|1]);
}
void build(int l,int r,int rt)
{
    dly[rt]=rev[rt]=0;
    if(l==r)
    {
        sum[rt]=(str[l]=='(')?-1:1;
        lmax[rt]=(sum[rt]<0)?0:1;
        lmin[rt]=(sum[rt]<0)?-1:0;
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt);
}
void updata(int L,int R,int op,int l,int r,int rt)
{
    if(L<=l&&R>=r)
    {
        if(op)set(rt,op,r-l+1);
        else reverse(rt);
        return;
    }
    int m=(l+r)>>1;
    pushdown(rt,m-l+1,r-m);
    if(L<=m)updata(L,R,op,lson);
    if(R>m)updata(L,R,op,rson);
    pushup(rt);
}
int querys(int L,int R,int l,int r,int rt)
{
    if(L<=l&&R>=r)return sum[rt];
    int m=(l+r)>>1,ret=0;
    pushdown(rt,m-l+1,r-m);
    if(L<=m)ret+=querys(L,R,lson);
    if(R>m)ret+=querys(L,R,rson);
    pushup(rt);
    return ret;
}
int querym(int L,int R,int l,int r,int rt)
{
    if(L<=l&&R>=r)return lmax[rt];
    int m=(l+r)>>1,ret;
    pushdown(rt,m-l+1,r-m);
    if(R<=m)ret=querym(L,R,lson);
    else if(L>m)ret=querym(L,R,rson);
    else ret=max(querym(L,R,lson),querys(L,R,lson)+querym(L,R,rson));
    pushup(rt);
    return ret;
}
int main()
{
    int i,j,n,m,t,cs;
    char op[55];
    scanf("%d",&t);
    for(cs=1;cs<=t;++cs)
    {
        printf("Case %d:\n",cs);
        scanf("%d%s%d",&n,str,&m);
        build(0,n-1,1);
        while(m--)
        {
            scanf("%s%d%d",op,&i,&j);
            if(op[0]=='s')
            {
                scanf("%s",op);
                updata(i,j,op[0]=='('?-1:1,0,n-1,1);
            }
            else if(op[0]=='r')updata(i,j,0,0,n-1,1);
            else if(!querys(i,j,0,n-1,1)&&!querym(i,j,0,n-1,1))puts("YES");
            else puts("NO");
        }
        puts("");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值