题目描述
我们定义一个括号序列是高兴的,当且仅当它右括号的个数不超过左括号的个数。
现在你要维护一个长度为 $n$ 的括号序列,编号为 $1−n$ ,支持以下三种操作:
1 $p$ 表示将第 $p$ 个括号反向,既 $($ 变为 $)$ , $)$ 变为 $($ 。
2 $l$ $r$ 表示询问:如果要使第 $l$ 个括号到第 $r$ 个括号组成的序列的每个前缀都是高兴的,至少需要删去多少个位置上的括号。
3 $l$ $r$ 表示询问:如果要使第 $l$ 个括号到第 $r$ 个括号组成的序列的每个前缀和每个后缀都是高兴的,至少需要删去多少个位置上的括号。
数据范围
$n \le 2 \times 10^5,q \le 3 \times 10^5$
题解
考虑将 $[l,r]$ 的括号序列拉出,做个前缀和,然后表示成一个函数图象
如果是2操作的话,显然是 $max(-min,0)$
对于3操作,考虑贪心,将前缀先弄成合法,此时图象会发生偏移,每个点 $i$ 最终变为 $x_i-min(x_j
)(j<i)$
要使得后缀变得合法,则右端点要成为图象的最高点,因此要使前缀合法后的max降至右端点后才合法
因此用线段树维护极差即可
代码
#include <bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,a[N],q;char ch;
struct T{int in,ax,s,df;}t[N<<2],V;
#define Ls k<<1
#define Rs k<<1|1
#define mid ((l+r)>>1)
T hb(T x,T y){
V.in=min(x.in,y.in+x.s);
V.ax=max(x.ax,y.ax+x.s);
V.df=max(max(x.df,y.df),y.ax+x.s-x.in);
V.s=x.s+y.s; return V;
}
void build(int k,int l,int r){
if (l==r){
if (~a[l]) t[k]=(T){0,1,1,1};
else t[k]=(T){-1,0,-1,0};
return;
}
build(Ls,l,mid);build(Rs,mid+1,r);
t[k]=hb(t[Ls],t[Rs]);
}
void upd(int k,int l,int r,int x){
if (l==r){
if (~a[l]) t[k]=(T){0,1,1,1};
else t[k]=(T){-1,0,-1,0};
return;
}
if (mid>=x) upd(Ls,l,mid,x);
else upd(Rs,mid+1,r,x);
t[k]=hb(t[Ls],t[Rs]);
}
T qry(int k,int l,int r,int L,int R){
if (L<=l && r<=R) return t[k];
if (mid>=R) return qry(Ls,l,mid,L,R);
if (mid<L) return qry(Rs,mid+1,r,L,R);
return hb(qry(Ls,l,mid,L,R),qry(Rs,mid+1,r,L,R));
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf(" %c",&ch),
a[i]=((ch=='(')?1:-1);
build(1,1,n);scanf("%d",&q);
for (int op,x,y;q--;){
scanf("%d%d",&op,&x);
if (op<2) a[x]=-a[x],upd(1,1,n,x);
else if (op<3){
scanf("%d",&y);
T v=qry(1,1,n,x,y);
printf("%d\n",max(0,-v.in));
}
else{
scanf("%d",&y);
T v=qry(1,1,n,x,y);
printf("%d\n",max(0,-v.in)+v.df-(v.s-v.in));
}
}
return 0;
}