刚刚学完 whk 时无聊看了下提交记录,发现这道富有启发意义的题目。
首先,注意到这实际上就是个序列的 《括号树》,拿来做就行,\(f_i\) 为以 \(i\) 结尾的合法括号串数量,\(f_i=f_{L_i-1}+1\),然后再做一遍前缀和,相减求出以区间 \([x,y]\) 为结尾的数量,但是我们发现会算重,具体的,算重了 \([1,x-1]\) 取后缀,\([x,y]\) 取前缀所组合成的合法串数量。
目前题目还有一个性质没用到,就是保证每次询问 \([x,y]\) 合法。那么,显然一定是以 \(x-1\) 为右端点的合法括号序列与以 \(x\) 为左端点的合法括号序列的组合。因为不可能出现一个合法的由这 \(2\) 个不合法的组成。若有,显然存在一个 \(p\in [x,y]\) 为右括号,需要匹配 \(q\in [1,x-1]\),那么 \([x,y]\) 不合法。
首先 \(f_{x-1}\) 即后缀合法括号序列,那么还要有 \([x,y]\) 的前缀合法然后相乘才对,于是我们期望找到一个能表示 \([x,y]\) 的前缀合法括号序列。
考虑对于每个前缀合法括号序列,一定是 \([x,p]\),那么因为 \([x,y]\) 合法,显然 \([p+1,y]\) 也合法。
于是,每个前缀合法括号序列都存在唯一一个以 \(y\) 为结尾,开头在 \([x,y]\) 的合法括号序列与之对应。(双射)
于是,我们现在期望通过 \(f\) 求后者。
发现倘若以 \(y\) 为右端点,但左端点在 \([1,x-1]\) 的合法括号序列,它一定可以划分为 \(s:[p,x-1]+[x,y]\)。否则,若左端点在 \([x,y]\) 则一定无法划分,即不会计入 \(f_{x-1}\),但 \(2\) 部分都会计入 \(f_y\)。
综上,减去 \(f_{x-1}*(f_y-f_{x-1})\)。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int rd() {
int f=1,sum=0; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
const int N=(int)(3e5+5);
char s[N];
int n,m,f[N],st[N],sum[N],top;
signed main() {
n=rd(); m=rd(); scanf("%s",s+1);
for(int i=1;i<=n;i++) {
if(s[i]=='(') st[++top]=i;
else {
if(!top) continue ;
f[i]=f[st[top]-1]+1; --top;
}
}
for(int i=1;i<=n;i++) {
sum[i]=sum[i-1]+f[i];
// printf("%d ",f[i]);
}
while(m--) {
rd(); int x=rd(),y=rd();
printf("%lld\n",sum[y]-sum[x-1]-f[x-1]*(f[y]-f[x-1]));
// [1,x-1]*[x,y] ,[x,y]=[y,x]
}
return 0;
}
标签:Upgrade,前缀,CF1625E1,ch,合法,括号,Cats,端点,序列
From: https://www.cnblogs.com/xugangfan/p/16599775.html