现在的位置: 首页 > 综合 > 正文

UESTC 1546 Bracket Sequence(线段树 成段更新)

2013年10月15日 ⁄ 综合 ⁄ 共 4788字 ⁄ 字号 评论关闭

题目大意:对于一个括号序列,有两种操作。set操作:将区间内的数置同;reverse:将区间内的数取反。对于每个询问,判断该区间的括号是否匹配。

思路:数据规模100000,要在O(logn)或者O(1)内完成操作,线段树维护信息和查询。

对于每个询问,需要O(1)的判断括号是否匹配:将‘(’看做-1,‘)’看做1,从左到右扫一遍,记录连续和最大值,和整个区间的和,只要 区间和==0 && 连续和最大值<=0,则括号匹配。

但是有一个问题:当reverse的操作做完之后,无法在O(1)的时间内更新区间连续和的最大值,因为该最大值恰巧是未操作之前的区间连续和最小值。所以线段树还要维护这个信息。

看看线段树维护的信息:区间和sum,区间连续和最大值mx,区间连续和最小值mi,reverse操作的懒惰标记,set操作的懒惰标记


PS:对于两个懒惰标记的处理,可能是个人原因,我搜到的程序自己看的不大懂,所以自己写了一个很一般的程序。或许是数据比较弱,感觉自己写的可能会有BUG,希望如果读到有错误的地方,予以指正。

对于更新set操作,当他遇到reverse的懒惰标记的时候,可以完全将该标记覆盖,因为无论前面的操作对数列有什么影响,set操作把它们全变成同一个数。对于更新reverse操作,当他遇到set懒惰标记时,实际上的功用就是将set懒惰标记的值变为相反数(想想为什么),比如set标记的值为1,就是将该区间全变为1,想在有个revers操作,不就是将该区间全变为-1么,将1改为-1就ok。还有就是,当他遇到reverse懒惰标记的时候,取个异或值,因为对一个区间做两次reverse操作,相当于什么也没做。

这样做的时候,其实不会出现在同一个区间,reverse懒惰标记和set懒惰标记同时出现。也就是说,两者只会出现一个。(这一块儿就是我看别人的代码看不懂的地方,别人都是两个一块儿考虑的)

更新操作:父亲->sum=左儿子->sum + 右儿子->sum;  

                    父亲->mx=max ( 左儿子->mx,左儿子->sum + 右儿子->mx);

                    父亲->mi=min ( 左儿子->mi,左儿子->sum + 右儿子->mi);

                   reverse操作: mx=-mi; mi=-mx; sum=-sum;

#include <iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
using namespace std;
const int maxn=100000+10;
struct Node
{
    int l,r;
    int sum;
    int mx,mi;
    int lz_r,lz;//lz_r是reverse操作的懒惰标记,lz是set操作的懒惰标记
}node[maxn << 2];
char s[maxn];
int v[maxn];
void push_up(int p,int ls,int rs)
{
    node[p].sum=node[ls].sum+node[rs].sum;
    node[p].mx=max(node[ls].mx,node[rs].mx+node[ls].sum);
    node[p].mi=min(node[ls].mi,node[rs].mi+node[ls].sum);
}
void push_down(int p,int ls,int rs)//结点,左儿子,右儿子
{
    if(node[p].lz!=0)//如果该区间有set的懒惰标记
    {
        node[ls].sum=(node[ls].r-node[ls].l+1)*node[p].lz;
        node[ls].mi=min(node[p].lz,node[ls].sum);
        node[ls].mx=max(node[p].lz,node[ls].sum);
        node[ls].lz=node[p].lz;
        if(node[ls].lz_r!=0)node[ls].lz_r=0;//lz向左右传递的时候顺便覆盖之前reverse懒惰标记的值

        node[rs].sum=(node[rs].r-node[rs].l+1)*node[p].lz;
        node[rs].mi=min(node[p].lz,node[rs].sum);
        node[rs].mx=max(node[p].lz,node[rs].sum);
        node[rs].lz=node[p].lz;
        if(node[rs].lz_r!=0)node[rs].lz_r=0;

        node[p].lz=0;//释放该节点的标记值
    }
    else if(node[p].lz_r!=0)//如果有reverse懒惰标记
    {
        node[ls].sum=-node[ls].sum;
        int temp=node[ls].mi;
        node[ls].mi=-node[ls].mx;
        node[ls].mx=-temp;
        if(node[ls].lz!=0)//将set懒惰标记置反,同时reverse标记清零
        {
            node[ls].lz=-node[ls].lz;
            node[ls].lz_r=0;
        }
        else node[ls].lz_r^=1;//如果遇到reverse标记,取异或值

        node[rs].sum=-node[rs].sum;
         temp=node[rs].mi;
        node[rs].mi=-node[rs].mx;
        node[rs].mx=-temp;
        if(node[rs].lz!=0)
        {
            node[rs].lz=-node[rs].lz;
            node[rs].lz_r=0;
        }
        else node[rs].lz_r^=1;

        node[p].lz_r=0;
    }
}
void make_tree(int p,int a,int b)//建树区间1~n,所以操作的时候区间端点加1
{
    node[p].l=a;node[p].r=b;
    node[p].lz=node[p].lz_r=0;
    if(a==b)
    {
        node[p].sum=node[p].mi=node[p].mx=v[a];
        return;
    }
    int ls=p << 1,rs=ls+1;
    int mid=(node[p].l+node[p].r) >> 1;
    make_tree(ls,a,mid);
    make_tree(rs,mid+1,b);
    push_up(p,ls,rs);
}
void modify_set(int p,int a,int b,int x)//set操作
{
    if(node[p].l==a && node[p].r==b)//如果找到该区间
    {
        node[p].sum=(b-a+1)*x;
        node[p].mi=min(x,node[p].sum);
        node[p].mx=max(x,node[p].sum);
        node[p].lz=x;
        if(node[p].lz_r!=0)node[p].lz_r=0;//将reverse的标记覆盖
        return;
    }
    int ls=p << 1,rs=ls+1;
    int mid=(node[p].l+node[p].r) >> 1;
    push_down(p,ls,rs); //向下释放懒惰标记
    if(b<=mid)modify_set(ls,a,b,x);
    else if(a>=mid+1)modify_set(rs,a,b,x);
    else
    {
        modify_set(ls,a,mid,x);
        modify_set(rs,mid+1,b,x);
    }
    push_up(p,ls,rs);
}
void modify_rev(int p,int a,int b)//reverse操作
{
    if(node[p].l==a && node[p].r==b)
    {
        node[p].sum=-node[p].sum;
        int temp=node[p].mi;
        node[p].mi=-node[p].mx;
        node[p].mx=-temp;
        if(node[p].lz!=0)//改变set标记
        {
            node[p].lz=-node[p].lz;
            node[p].lz_r=0;
        }
        else//或者与之前的reverse操作取异或值
        {
            node[p].lz_r^=1;
        }
        return;
    }
    int ls=p << 1,rs=ls+1;
    int mid=(node[p].l+node[p].r) >> 1;
    push_down(p,ls,rs);
    if(b<=mid)modify_rev(ls,a,b);
    else if(a>=mid+1)modify_rev(rs,a,b);
    else
    {
        modify_rev(ls,a,mid);
        modify_rev(rs,mid+1,b);
    }
    push_up(p,ls,rs);
}
int query_sum(int p,int a,int b)
{
    if(node[p].l==a && node[p].r==b)
    {
        return node[p].sum;
    }
    int ls=p << 1,rs=ls+1;
    int mid=(node[p].l+node[p].r) >> 1;
    push_down(p,ls,rs);
    if(b<=mid)return query_sum(ls,a,b);
    else if(a>=mid+1)return query_sum(rs,a,b);
    else return query_sum(ls,a,mid)+query_sum(rs,mid+1,b);
}
int query_max(int p,int a,int b)
{
    if(node[p].l==a && node[p].r==b)
    {
        return node[p].mx;
    }
    int ls=p << 1,rs=ls+1;
    int mid=(node[p].l+node[p].r) >> 1;
    push_down(p,ls,rs);
    if(b<=mid)return query_max(ls,a,b);
    else if(a>=mid+1)return query_max(rs,a,b);
    else return max(query_max(ls,a,mid),query_sum(ls,a,mid)+query_max(rs,mid+1,b));
    //一定是左区间的和+右区间的max值,wa了2次
}
void  convert(char *s,int n)//转换成数组
{
    for(int i=0;i<n;i++)
    {
        if(s[i]=='(')v[i+1]=-1;
        else v[i+1]=1;
    }
}
int main()
{
    int t,n,m,a,b;
    char ss[10],ch[2];
    //freopen("in","r",stdin);
    scanf("%d",&t);
    for(int kk=1;kk<=t;kk++)
    {
        printf("Case %d:\n",kk);
        scanf("%d",&n);
        scanf("%s",s);
        convert(s,n);
        make_tree(1,1,n);
        scanf("%d",&m);
        while(m--)
        {
            scanf("%s",ss);
            if(ss[0]=='s')
            {
                scanf("%d %d %s",&a,&b,ch);
                int ans=ch[0]=='('?-1:1;
                modify_set(1,a+1,b+1,ans);
            }
            else if(ss[0]=='r')
            {
                scanf("%d %d",&a,&b);
                modify_rev(1,a+1,b+1);
            }
            else if(ss[0]=='q')
            {
                scanf("%d %d",&a,&b);a++;b++;
                int ans_sum=query_sum(1,a,b);
                int ans_max=query_max(1,a,b);
                if(ans_sum==0 && ans_max<=0)printf("YES\n");
                else printf("NO\n");
            }
        }
        printf("\n");
    }
    return 0;
}

抱歉!评论已关闭.