ACM-ICPC Live Archive: 6838

要想使括号匹配,sum数组的值要都大于0

//struct A
//{
//    int x,y;
//    bool operator < (const A & a) const
//    {
//        return x>a.x;
//    }
//}; //优先队列(按结构体中的x从小到大排序)
//priority_queue <A> q;
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <map>
#include <set>
#define eps 0.0000000001
#define read(a) scanf("%d",&a)
#define mem(a) memset(a,0,sizeof(a))
#define maxx 1e10
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
//priority_queue<int,vector<int>,greater<int> > q;
#define mod 1000000007
const int maxn=300100;
set<int> st;
char cs[maxn];
int a[maxn];
int sum[maxn*4];
int Add[maxn*4];//懒惰标记
void PushUp(int n)
{
    sum[n]=min(sum[n*2],sum[n*2+1]);
    return;
}
//建树
void Build(int l,int r,int num)
{
    if(l==r)
    {
        sum[num]=a[l];
        return;
    }
    int m=(l+r)/2;
    //左右递归
    Build(l,m,num*2);
    Build(m+1,r,num*2+1);
    //该结点值等于左右孩子结点值的和
    PushUp(num);
    return;
}

//点更新  假设a[x]+=s;
void Update1(int x,int s,int l,int r,int num)
{
    if(l==r)
    {
        sum[num]+=s;
        return;
    }
    int m=(l+r)/2;
    //根据m和x的位置关系判断调用左子树还是右子树
    if(x<=m)//左
        Update1(x,s,l,m,num*2);
    else
        Update1(x,s,m+1,r,num*2+1);
    PushUp(num);
}

// 下推标记函数
void PushDown(int num,int ln,int rn)//ln和rn是左子树和右子树的数量
{
    if(Add[num]) //如果被标记了
    {
        Add[num*2]+=Add[num];
        Add[num*2+1]+=Add[num];// 把标记推到左右子树
        //修改子节点的Sum使之与对应的Add相对应
        sum[num*2]+=Add[num];
        sum[num*2+1]+=Add[num];
        Add[num]=0;
    }
    return;
}

//区间更新  假设L到R 加C
void Update2(int L,int R,int C,int l,int r,int num)
{
    if(l>=L&&R>=r)
    {
        sum[num]+=C;
        Add[num]+=C;//增加Add标记,表示本区间的Sum正确,子区间的Sum仍需要根据Add的值来调整
        return;
    }
    int m=(l+r)/2;
    PushDown(num,m-l+1,r-m);  //下推标记
    if(L<=m)
        Update2(L,R,C,l,m,num*2);
    if(R>=m+1)
        Update2(L,R,C,m+1,r,num*2+1);
    PushUp(num);
    return;
}

//区间查询  查询区间 L-R
int Query(int L,int R,int l,int r,int num)
{
    if(L<=l&&R>=r) //l,r在L,R内
    {
        return sum[num];
    }
    int m=(l+r)/2;
    PushDown(num,m-l+1,r-m);
    int ans=inf;
    if(L<=m)
        ans=min(ans,Query(L,R,l,m,num*2));
    if(R>=m+1)
        ans=min(ans,Query(L,R,m+1,r,num*2+1));
    return ans;
}

int erfen(int x,int n,int mini)
{
    int l=1,r=x-1,m,ans=x;
    while(l<=r)   //二分
    {
        m=(l+r)/2;
        if(Query(m,x-1,1,n,1)>=mini) //找到最小值大于2的区间
        {//如果右区间最小值大于等于mini,就把右端点向左缩
            ans=m;
            r=m-1;
        }
        else//否则,就把左端点舍掉
            l=m+1;
    }
    return ans;
}

int main()
{
    int n,m;
    while(~scanf("%d %d",&n,&m))
    {
        scanf("%s",cs+1);
        a[1]=0;
        st.clear();
        for(int i=1;i<=n;i++)
        {
            if(cs[i]=='(')
                a[i]=a[i-1]+1;
            else
            {
                a[i]=a[i-1]-1;
                st.insert(i);
            }
        }
//        for(int i=1;i<=n;i++)
//            cout<<a[i]<<" ";
        mem(Add);
        Build(1,n,1);
        int x,ans;
        for(int i=0;i<m;i++)
        {
            read(x); //x是反转的位置
            if(cs[x]==')')
            {
                ans=erfen(x,n,2);
                if(ans<x)
                {
                    Update2(ans,x-1,-2,1,n,1);//sum数组中x后面的的值已经被两次翻转抵消了
                    cs[x]='(';cs[ans]=')';   //所以只需要把ans和x中间的部分减掉就好
                    st.insert(ans);
                    st.erase(x);
                }
                //else的情况就是在原位置再反转回来
                // ans==x
            }
            else//上一个是因为要减2,要保证sum数组的值不为0,所以不能减小于2的部分
            {//因为我需要把右括号翻转成左括号,sum数组的值要加2,所以没有限制条件
                //直接找到第一个翻转就好
                ans=*st.begin();
                if(ans>x)
                    ans=x;
                if(ans<x)
                {
                    Update2(ans,x-1,2,1,n,1);  //同上一个更新
                    cs[x]=')';cs[ans]='(';
                    st.insert(x);
                    st.erase(ans);
                }
            }
            cout<<ans<<endl;
        }

    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Septembre_/article/details/88648682