蓝桥杯 翻转括号序列 题解(线段树 思维)

题目链接

题目大意

给定一个长度为 \(n\) 的括号序列,要求支持两种操作:

  1. \([L,R]\) 区间内的括号全部翻转
  2. 求出以\(L\)为左端点时,最长的合法括号序列对应的\(R\)

\(1 ≤ n ≤ 10^6 , 1 ≤ m ≤ 2 × 10^5\)

题目思路

首先根据这个数据范围,要想到线段树

\(s[i]==( \;a[i]=1\)

\(s[i]==) \;a[i]=-1\)

一个显然的想法是建立一个线段树,其节点的值为\(a\)数组的前缀和

那么对于操作\(1\)如何修改是一个问题,你会发现不好直接进行修改

可以把这个问题转化为\([1,l-1]\)\([1,r]\)修改两次,显然是等价的

那么修改\([1,x]\)会使得\([1,x]\)中的元素乘以\(-1\),使得\([x+1,n]\)中的所有元素\(+2pre[x]\)

对于操作2显然是要找到在\([l,n]\)中找到一个最大的\(pos\)使得\(pre[pos]==pre[l-1]\)

并且在\([l,pos-1]\)中不存在\(pre[i]<pre[pos]\)

这个问题比较麻烦,解决的方法也比较巧妙

首先找到\([l,n]\)中第一个小于\(pre[l-1]\)\(pos\),那么答案必定在\([l,pos-1]\)

再找到\([1,pos-1]\)中坐标最接近\(pos-1\)且值小于\(pre[l-1]+1\)的位置

如果这个位置大于\(l\)显然就是答案,否则为\(0\)

如何寻找\(pos\)就是线段树上二分,需要维护区间的最大最小值

这个题目要关注的是有两个\(lazy\)相互影响要注意写法

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,m;
int a[maxn];
int mi[maxn<<2],ma[maxn<<2];
int lazy[maxn<<2][3];
char s[maxn];
void updown(int node){
    if(lazy[node][1]){
        lazy[node<<1][1]^=1;
        lazy[node<<1|1][1]^=1;
        swap(mi[node<<1],ma[node<<1]);
        mi[node<<1]*=-1,ma[node<<1]*=-1;
        swap(mi[node<<1|1],ma[node<<1|1]);
        mi[node<<1|1]*=-1,ma[node<<1|1]*=-1;
        // 一定要写下面这句话 加的东西也发生了改变
        lazy[node<<1|1][2]*=-1;
        lazy[node<<1][2]*=-1;
        lazy[node][1]=0;
    }
    if(lazy[node][2]){
        lazy[node<<1][2]+=lazy[node][2];
        lazy[node<<1|1][2]+=lazy[node][2];
        mi[node<<1]+=lazy[node][2],ma[node<<1]+=lazy[node][2];
        mi[node<<1|1]+=lazy[node][2],ma[node<<1|1]+=lazy[node][2];
        lazy[node][2]=0;
    }
}
void push_up(int node){
    mi[node]=min(mi[node<<1],mi[node<<1|1]);
    ma[node]=max(ma[node<<1],ma[node<<1|1]);
}
void build(int node,int l,int r){
    if(l==r){
        mi[node]=ma[node]=a[l];
        return ;
    }
    int mid=(l+r)/2;
    build(node<<1,l,mid);
    build(node<<1|1,mid+1,r);
    push_up(node);
}
void update(int node,int L,int R,int l,int r,int add,int op){
    if(L>R) return ;
    // 边界处理
    if(L<=l&&r<=R){
        if(op==1){
            swap(mi[node],ma[node]);
            mi[node]*=-1;
            ma[node]*=-1;
            lazy[node][op]^=1;
            // 一定要写下面这句话 加的东西也发生了改变
            lazy[node][2]*=-1;
        }else if(op==2){
            mi[node]+=add;
            ma[node]+=add;
            lazy[node][op]+=add;
        }
        return ;
    }
    updown(node);
    int mid=(l+r)/2;
    if(mid>=L) update(node<<1,L,R,l,mid,add,op);
    if(mid<R)  update(node<<1|1,L,R,mid+1,r,add,op);
    push_up(node);
}
int query(int node,int l,int r,int pos){
    if(pos==0||pos==n+1){// 边界处理
        return 0;
    }
    if(l==r){
        return ma[node];
    }
    updown(node);
    int mid=(l+r)/2,now=0;
    if(mid>=pos) now=query(node<<1,l,mid,pos);
    if(mid<pos)  now=query(node<<1|1,mid+1,r,pos);
    return now;
}
int query_serch1(int node,int l,int r,int pos,int val){
    if(l==r){
        return l;
    }
    int mid=(l+r)/2;
    int ans=0;
    updown(node);
    if(mid>=pos&&mi[node<<1]<val){
        ans=query_serch1(node<<1,l,mid,pos,val);
    }
    if(ans==0&&mi[node<<1|1]<val){
        ans=query_serch1(node<<1|1,mid+1,r,pos,val);
    }
    return ans;
}
int query_serch2(int node,int l,int r,int pos,int val){
    if(l==r){
        return l;
    }
    int mid=(l+r)/2;
    int ans=0;
    updown(node);
    if(mid+1<=pos&&mi[node<<1|1]<val){
        ans=query_serch2(node<<1|1,mid+1,r,pos,val);
    }
    if(ans==0&&mi[node<<1]<val){
        ans=query_serch2(node<<1,l,mid,pos,val);
    }
    return ans;
}
signed main(){
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++){
        if(s[i]=='(') a[i]=1;
        else a[i]=-1;
        a[i]+=a[i-1];
    }
    build(1,1,n);
    for(int i=1,opt,l,r;i<=m;i++){
        scanf("%d%d",&opt,&l);
        if(opt==1){
            scanf("%d",&r);
            // [1,l-1]
            update(1,1,l-1,1,n,0,1);
            int sum=query(1,1,n,l-1);
            update(1,l,n,1,n,2*sum,2);
            // [1,r]
            update(1,1,r,1,n,0,1);
            sum=query(1,1,n,r);
            update(1,r+1,n,1,n,2*sum,2);
        }else{
            int sum=query(1,1,n,l-1);// 要找pre[l-1]
            int lim=query_serch1(1,1,n,l,sum);

            // 找到[l,n]中第一个比sum小的值
            if(lim==0) lim=n+1;
            // 找到[1,lim-1]中比sum+1小最接近lim-1的值
            int ans=query_serch2(1,1,n,lim-1,sum+1);
            if(ans>l){
                printf("%d\n",ans);
            }else{
                printf("0\n");
            }
        }
    }
    return 0;
}

posted @ 2022-02-25 18:20  hunxuewangzi  阅读(397)  评论(0编辑  收藏  举报