CSU 1542 Flipping Parentheses

题目链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1542


题意:给出一串匹配的括号,改变其中一个括号的方向,要求改变最左边的一个括号方向使得该串括号重新匹配


思路:((()))这样一个串括号,我们每遇到‘(’便加1,遇到’)‘便建1这样可以得到序列:1 2 3 2 1 0,可以发现如果一串括号是匹配的,那么该序列的末尾必定是0且序列的每一位都大于0

           如果我们将某个'('变为‘)’的话,序列中该位置和其后面的数全都-2,要使序列满足末尾是0且序列的每一位都大于0的条件,我们只需要找到最左边的一个‘)’括号将其变为’(‘就可以了

           如果我们将某个‘)’变成‘(’的话,序列中该位置和其后面的数全都-2,要使序列满足末尾是0便可以使括号重新匹配,但是如果随便翻转一个'('的话可能会导致序列出现负数,因此我们需要找到一个[L,N],里面所有的数都大于等于2,我们翻转第L-1个括号便可以( 因为L是大于等于2的而L-1小于等于2,所有L必定是‘(’ )

           用线段树来维护这个序列,第一种情况维护f=A[i]-i,如果是‘)’f[i]将小于0,第二种情况维护区间最小值minx就可以

           一般来说数组开到maxn*3左右应该就可以了,但是这道题蜜汁re,开了maxn*4就过了,大概是我线段树写法的问题?


#include <iostream>
 #include <cstdio>
 #include <algorithm>
 #include <cstring>
 #define maxn 330000
 using namespace std;

 struct Tree
 {
     int l,r,date;
     int minx,f;
 }tree[maxn*4];

int lazy[maxn*4],s[maxn];
char str[maxn];

void push_up(int root)
{
    tree[root].f=min(tree[root<<1].f,tree[root<<1|1].f);
    tree[root].minx=min(tree[root<<1].minx,tree[root<<1|1].minx);
}


void build(int root,int l,int r)
{
    tree[root].l=l;
    tree[root].r=r;
    if (l==r)
    {
        tree[root].f=s[l]-l;
        tree[root].minx=s[l];
        return;
    }
    int mid=(l+r)>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);
    push_up(root);
}

void push_down(int root)
{
    if (lazy[root]!=0)
    {
        tree[root<<1].minx+=lazy[root];
        tree[root<<1].f+=lazy[root];
        lazy[root<<1]+=lazy[root];

        tree[root<<1|1].minx+=lazy[root];
        tree[root<<1|1].f+=lazy[root];
        lazy[root<<1|1]+=lazy[root];

        lazy[root]=0;
    }
}

void update(int root,int l,int r,int p)
{
    if (tree[root].l>=l && tree[root].r<=r)
    {
        tree[root].f+=p;
        tree[root].minx+=p;
        lazy[root]+=p;
        return;
    }
    push_down(root);
    int mid=(tree[root].l+tree[root].r)>>1;
    if (l<=mid) update(root<<1,l,r,p);
    if (r>mid) update(root<<1|1,l,r,p);
    push_up(root);
}

int query1(int root)
{
    if (tree[root].l==tree[root].r)
    {
        return tree[root].l;
    }
    push_down(root);
    if (tree[root<<1].f<0) return query1(root<<1);
    else return query1(root<<1|1);
}

int query2(int root)
{
    if (tree[root].l==tree[root].r)
    {
        return tree[root].l;
    }
    push_down(root);

    if (tree[root<<1|1].minx< 2) return query2(root<<1|1);
    else return query2(root<<1);
}


int main()
{
    int n,m;
    while (scanf("%d%d",&n,&m)!=EOF)
    {
        scanf("%s",str+1);
        memset(s,0,sizeof(s));
        memset(tree,0,sizeof(tree));
        memset(lazy,0,sizeof(lazy));
        int len=strlen(str+1);
        for (int i=1;i<=len;i++)
        {
            if (str[i]=='(')
                s[i]=s[i-1]+1;
            else
                s[i]=s[i-1]-1;
        }
        build(1,1,len);

        for (int i=0;i<m;i++)
        {
            int k;
            scanf("%d",&k);

            if (str[k]==')')
            {
                update(1,k,len,2);
                str[k]='(';
                int res=query2(1)+1;
                update(1,res,len,-2);
                str[res]=')';
                printf("%d\n",res);
            }
            else
            {
                update(1,k,len,-2);
                str[k]=')';
                int res=query1(1);
                update(1,res,len,2);
                str[res]='(';
                printf("%d\n",res);
            }
        }
    }
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值