[括号] [计数] [树] CF1625E2 Cats on the Upgrade (hard version)
先说
显然对每个右括号求出其对应的左括号下标
然后题目保证询问
这是个分治的形式,递推计算就好了。具体地,暴力找到
因为每个大串至多被算一次,所以复杂度
接着是
容易发现分治的过程呈树状。令
那么修改是删除叶子,查询是求两个兄弟节点构成区间的方案数。
但是发现这玩意不好维护啊,如果树剖维护
由于一次修改至多改变一个
用树状数组,修改操作是单点改,查询
复杂度
总结:主要难度在转化成树上问题、以及树上信息的维护,需要一定计数技巧。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 5;
int n, q, lst[N], nxt[N], stk[N], tp, opt, l, r;
int son[N], dfn[N], df, ed[N], _dfn[N], _df, fa[N], br[N];
char c[N];
vector<int> to[N];
struct BIT{
int t[N];
inline void init() {memset(t, 0, sizeof t);}
inline void upd(int a, int k) {for(; a < N; a += a & -a) t[a] += k;}
inline int qry(int a) {int res = 0; for(; a; a -= a & -a) res += t[a]; return res;}
inline int fid(int l, int r) {if(l > r) return 0; return qry(r) - qry(l - 1);}
}A, B;
inline void dfs(int u){
dfn[u] = ++df;
for(auto v : to[u]) ++son[u], A.upd(_dfn[v] = ++_df, 1);
for(auto v : to[u]) fa[v] = u, br[v] = df + 1, dfs(v);
ed[u] = df, B.upd(dfn[u], son[u] * (son[u] + 1) / 2);
}
signed main(){
scanf("%lld%lld%s", &n, &q, c + 1);
for(int i = 1; i <= n; ++i)
if(c[i] == '(') stk[++tp] = i;
else if(tp){
lst[i] = stk[tp--], nxt[lst[i]] = i;
int j = i - 1;
while(j > lst[i]) to[i].push_back(j), j = lst[j] - 1;
reverse(to[i].begin(), to[i].end());
}
for(int i = n, p = n + 1; i; --i) if(i <= p && lst[i]) p = lst[i], to[n + 1].push_back(i);
reverse(to[n + 1].begin(), to[n + 1].end());
dfs(n + 1);
while(q--){
scanf("%lld%lld%lld", &opt, &l, &r);
if(opt == 1){
A.upd(_dfn[r], -1);
B.upd(dfn[fa[r]], son[fa[r]] * (son[fa[r]] - 1) / 2 - son[fa[r]] * (son[fa[r]] + 1) / 2), --son[fa[r]];
}
else{
int res = B.fid(br[nxt[l]], ed[r]), cnt = A.fid(_dfn[nxt[l]], _dfn[r]);
res += cnt * (cnt + 1) / 2;
printf("%lld\n", res);
}
}
return 0;
}