codeforces gym 100803G Flipping Parentheses 线段树

https://vjudge.net/contest/285175#problem/J
在这里插入图片描述在这里插入图片描述题目大意:给一个只包含 ( “(” ) “)” 的字符串 s s ,且保证它是平衡的(就是括号匹配的意思)。然后有 q q 个询问,第 i i 次询问把 s [ q i ] s[q_i] 进行翻转,让你求出最小的下标 p o s pos ,使得翻转 s [ p o s ] s[pos] 后,字符串 s s 的括号依然是平衡的。

思路:假设 ( “(” 的值为 1 1 ) “)” 的值为 1 -1 ,那么我们就把字符串 s s 变成了数的序列,用线段树维护这个序列的前缀和,以 ( ( ( ) ) ) ((())) 为例,它每个位置的值分别为 1 2 3 2 1 0 1、2、3、2、1、0 。然后考虑翻转操作,若 s [ q i ] = ( s[q_i]=“(” ,对其进行翻转,相当于把区间 [ q i , n ] [q_i,n] 的值都减去 2 2 ,反之相当于把区间 [ q i , n ] [q_i,n] 的值都加上 2 2 ;且对于第一个操作,我们找到第一个 ) “)” 进行翻转就可以了;对于第二个操作,我们需要从后往前找到最靠前的位置 p o s pos ,满足 a [ p o s n ] > = 2 a[pos…n]>=2 。我们引入一个值 v [ i ] = a [ i ] i v[i]=a[i]-i ,若 v [ i ] < 0 v[i]<0 ,则说明 [ 1 , i ] [1,i] 内至少有 1 1 ) “)” ,所以第一个操作的答案就是最靠前的位置 p o s pos ,满足 v [ p o s ] < 0 v[pos]<0 。区间修改、区间查询,用线段树就好了,详见代码。
t i p s : tips: 由于上述操作的区间的特殊性,查询代码中对区间的修改也是很特殊的,看不懂的话不妨看一下函数调用,然后仔细思考一下。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

const int maxn=3e5+5;

struct Tree
{
    int l,r,m1,m2,lazy;
}tree[maxn<<2];

int n,q;
int a[maxn];
char s[maxn];

inline void up(int i)
{
    tree[i].m1=min(tree[i<<1].m1,tree[i<<1|1].m1);
    tree[i].m2=min(tree[i<<1].m2,tree[i<<1|1].m2);
}

inline void down(int i)
{
    int l=i<<1,r=i<<1|1,v=tree[i].lazy;
    tree[l].lazy+=v,tree[r].lazy+=v;
    tree[l].m1+=v,tree[r].m1+=v;
    tree[l].m2+=v,tree[r].m2+=v;
    tree[i].lazy=0;
}

void build(int i,int l,int r)
{
    tree[i].l=l,tree[i].r=r,tree[i].lazy=0;
    if(l==r)
    {
        tree[i].m1=a[l]-l;
        tree[i].m2=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    up(i);
}

void update(int i,int l,int r,int v)
{
    if(tree[i].l==l&&tree[i].r==r)
    {
        tree[i].m1+=v;
        tree[i].m2+=v;
        tree[i].lazy+=v;
        return ;
    }
    if(tree[i].lazy)
        down(i);
    int mid=(tree[i].l+tree[i].r)>>1;
    if(r<=mid)
        update(i<<1,l,r,v);
    else if(l>mid)
        update(i<<1|1,l,r,v);
    else
        update(i<<1,l,mid,v),update(i<<1|1,mid+1,r,v);
    up(i);
}

int query1(int i,int l,int r)//寻找[l,r]内 最靠左的 m1<0 的位置
{
    if(l==r)
        return l;
    if(tree[i].lazy)
        down(i);
    int mid=(tree[i].l+tree[i].r)>>1;
    if(tree[i<<1].m1<0)
        return query1(i<<1,l,min(mid,r));
    else
        return query1(i<<1|1,mid+1,r);
}

int query2(int i,int l,int r)//查询[l,r]内最靠左的位置 pos 满足[pos,r]内的 m2 均>=2
{
    if(l==r) //l+1 才是满足题意的位置哦~
        return l+1;
    if(tree[i].lazy)
        down(i);
    int mid=(tree[i].l+tree[i].r)>>1;
    if(tree[i<<1|1].m2>=2&&l<=mid)
        return query2(i<<1,l,mid);
    else
        return query2(i<<1|1,mid+1,r);
}

int main()
{
    scanf("%d%d",&n,&q);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='(')
            a[i]=a[i-1]+1;
        else
            a[i]=a[i-1]-1;
    }
    build(1,1,n);
    int qi,id;
    while(q--)
    {
        scanf("%d",&qi);
        if(s[qi]=='(')
        {
            s[qi]=')',update(1,qi,n,-2);
            id=query1(1,1,qi);
            s[id]='(',update(1,id,n,2);
        }
        else
        {
            s[qi]='(',update(1,qi,n,2);
            id=query2(1,1,n);
            s[id]=')',update(1,id,n,-2);
        }
        printf("%d\n",id);
    }
    return 0;
}

发布了677 篇原创文章 · 获赞 30 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/xiji333/article/details/104724914