[括号] [计数] 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;
}

浙公网安备 33010602011771号