[括号] [计数] CF1625E Cats on the Upgrade (easy version)

posted on 2024-05-13 14:49:38 | under | source

显然对每个右括号求出其对应的左括号下标 \(lst_i\)\([lst_i,i]\) 是一段合法的括号子串,叫它们大串吧。

然后题目保证询问 \([l,r]\) 可拆成若干个大串,答案就是这些大串拼成的方案数 \(+\) 大串内部的方案数。

这是个分治的形式。即 \(f_i=\frac {cnt\times(cnt+1)}2+\sum f_j\)\(j\)\([lst_i, i]\) 中包含的大串。递推计算即可。

具体地,暴力找到 \(j\) 然后计算,查询的话预处理前缀和即可。

复杂度:每个大串只被遍历一次,是 \(O(n)\) 的。

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N = 3e5 + 5;
int n, q, s[N], lst[N], st[N], tp, f[N], g[N], gc[N];
int opt, l, r;
char c[N];

signed main(){
	scanf("%lld%lld%s", &n, &q, c + 1);
	for(int i = 1; i <= n; ++i){
		if(c[i] == '(') st[++tp] = i;
		else if(tp){
			lst[i] = st[tp--];
			int j = i - 1, cnt = 0;
			while(j > lst[i]) f[i] += f[j], ++cnt, j = lst[j] - 1;
			f[i] += cnt * (cnt + 1) / 2;
			g[i] = g[lst[i] - 1] + f[i];
			gc[i] = gc[lst[i] - 1] + 1;
		}
	}
	while(q--){
		scanf("%lld%lld%lld", &opt, &l, &r);
		int res = g[r] - g[l - 1], cnt = gc[r] - gc[l - 1];
		res += cnt * (cnt + 1) / 2;
		printf("%lld\n", res);
	}
	return 0;
} 
posted @ 2026-01-12 20:16  Zwi  阅读(1)  评论(0)    收藏  举报