CF1625E1 Cats on the Upgrade (easy version)

刚刚学完 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;
}
posted @ 2022-08-18 18:51  FxorG  阅读(20)  评论(0编辑  收藏  举报