拙者
题目描述
我们定义一个括号序列是高兴的,当且仅当它右括号的个数不超过左括号的个数。
现在你要维护一个长度为 n 的括号序列,编号为 1−n ,支持以下三种操作:
1 p 表示将第 p 个括号反向,既 ( 变为 ) , ) 变为 ( 。
2 l r 表示询问:如果要使第 l 个括号到第 r 个括号组成的序列的每个前缀都是高兴的,至少需要删去多少个位置上的括号。
3 l r 表示询问:如果要使第 l 个括号到第 r 个括号组成的序列的每个前缀和每个后缀都是高兴的,至少需要删去多少个位置上的括号。
数据范围
n≤2×105,q≤3×105
题解
考虑将 [l,r] 的括号序列拉出,做个前缀和,然后表示成一个函数图象
如果是2操作的话,显然是 max(−min,0)
对于3操作,考虑贪心,将前缀先弄成合法,此时图象会发生偏移,每个点 i 最终变为 xi−min(xj)(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; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步