Codeforces方向
Luogu方向
首先给出一个引理
一段序列去掉所有的匹配括号之后,剩下的序列一定组成一条链,且这条链的长度就是剩下序列的长度
可以粗糙地证明一下:
学过了树上莫队的知道:原题中的括号序列可以转化为欧拉序
树上的一条路径可以分成 2 种情况,记 first[x] 为 x 第一次在欧拉序中出现的位置, last[x] 同理,这里默认 first[u] < first[v]
其实上面画几个图感性理解一下就可以了
观察去掉匹配括号后,序列一定是 ))…)(…(( 类似这样的,其中 ( 和 ) 都可以没有
我们考虑把 ( 赋值为 1,) 赋值为 -1
那么路径的长度一定是找到一个点 p,从 p 往后的前缀和 - 从 p 往前的后缀和
从中找到的最大值
这个东西可以用线段树维护一下
想象需要记录那些东西:
前缀的最大相减值,后缀的最大相减值,把 [ l,r ] 填满的最大相减值, 总和,前缀和最大值,后缀和最小值,答案
通过这些值可以进行转移
#include
using namespace std;
const int N(200100);
int n,m;
char c[N];
inline int max3(int x,int y,int z){
return max(max(x,y),z);
}
inline int max4(int x,int y,int z,int k){
return max(max(x,y),max(z,k));
}
struct SegmentTree{//维护前缀和数组
struct Node{
int lmax,rmax,fillmax,sum,premx,sufmn,ans;
}seg[N<<2];
void pushup(int x){
seg[x].sum=seg[x<<1].sum+seg[x<<1^1].sum;
seg[x].fillmax=max(seg[x<<1].fillmax+seg[x<<1^1].sum,seg[x<<1^1].fillmax-seg[x<<1].sum);
seg[x].lmax=max3(seg[x<<1].lmax,seg[x<<1].fillmax+seg[x<<1^1].premx,seg[x<<1^1].lmax-seg[x<<1].sum);
seg[x].rmax=max3(seg[x<<1^1].rmax,seg[x<<1^1].fillmax-seg[x<<1].sufmn,seg[x<<1^1].sum+seg[x<<1].rmax);
seg[x].premx=max(seg[x<<1].premx,seg[x<<1].sum+seg[x<<1^1].premx);
seg[x].sufmn=min(seg[x<<1^1].sufmn,seg[x<<1^1].sum+seg[x<<1].sufmn);
seg[x].ans=max4(seg[x<<1].ans,seg[x<<1^1].ans,seg[x<<1].rmax+seg[x<<1^1].premx,seg[x<<1^1].lmax-seg[x<<1].sufmn);
}
void build(int l,int r,int x){
if(l==r){
if(c[l]=='(')
seg[x].lmax=seg[x].rmax=seg[x].fillmax=seg[x].sum=seg[x].premx=seg[x].ans=1,seg[x].sufmn=0;
else
seg[x].sum=seg[x].sufmn=-1,seg[x].fillmax=seg[x].lmax=seg[x].rmax=seg[x].ans=1,seg[x].premx=0;
return;
}
int mid=(l+r)>>1;
build(l,mid,x<<1),build(mid+1,r,x<<1^1);
pushup(x);
// cout<>1;
if(mid>=p)
modify(l,mid,x<<1,p,k);
else
modify(mid+1,r,x<<1^1,p,k);
pushup(x);
// cout<