蓝桥杯 翻转括号序列 题解(线段树 思维)
题目链接
题目大意
给定一个长度为 \(n\) 的括号序列,要求支持两种操作:
- 将\([L,R]\) 区间内的括号全部翻转
- 求出以\(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;
}
不摆烂了,写题