D. Invertible Bracket Sequences
A regular bracket sequence is a bracket sequence that can be transformed into a correct arithmetic expression by inserting characters '1' and '+' between the original characters of the sequence. For example:
- bracket sequences "()()" and "(())" are regular (the resulting expressions are: "(1)+(1)" and "((1+1)+1)");
- bracket sequences ")(", "(" and ")" are not.
Let's define the inverse of the bracket sequence as follows: replace all brackets '(' with ')', and vice versa (all brackets ')' with '('). For example, strings "()((" and ")())" are inverses of each other.
You are given a regular bracket sequence $s$. Calculate the number of pairs of integers $(l,r)$ ($1 \le l \le r \le |s|$) such that if you replace the substring of $s$ from the $l$-th character to the $r$-th character (inclusive) with its inverse, $s$ will still be a regular bracket sequence.
Input
The first line contains a single integer $t$ ($1 \le t \le 10^4$) — the number of test cases.
The only line of each test case contains a non-empty regular bracket sequence; it consists only of characters '(' and/or ')'.
Additional constraint on the input: the total length of the regular bracket sequences over all test cases doesn't exceed $2 \cdot 10^5$.
Output
For each test case, print a single integer — the number of pairs $(l,r)$ meeting the conditions from the statement.
Example
input
4
(())
()
()()()
(()())(())
output
1
0
3
13
Note
In the first example, there is only one pair:
- $(2, 3)$: (()) $\rightarrow$ ()().
In the second example, there are no pairs.
In the third example, there are three pairs:
- $(2, 3)$: ()()() $\rightarrow$ (())();
- $(4, 5)$: ()()() $\rightarrow$ ()(());
- $(2, 5)$: ()()() $\rightarrow$ (()());
解题思路
先给出我的做法,应该还有更简洁的方法,等官方题解出了再补。
如果一个括号序列是合法的,那么必然满足以下 $2$ 个条件:
- 在任意一个前缀中
(
的数量不小于)
的数量。 - 整个序列的
(
和)
数量相等。
现在把 (
和 )
分别看作 $1$ 和 $-1$,对序列求一个前缀和,记为 $s_i$。考虑枚举反转区间的左端点 $l$,那么哪些右端点 $r \, (i \leq r \leq n)$ 使得将区间 $[l,r]$ 内的括号反转后,整个括号序列仍是合法的?
先满足第 $1$ 个条件。如果反转区间 $[l,r]$,那么前缀会受到影响的位置就是 $k \in [l,r]$,下标为 $k$ 的前缀会变成 $s_{l-1} - (s_{k} - s_{l-1}) = 2 \cdot s_{l-1} - s_k$。如果第条件 $1$ 要满足,那么对于每个 $k$ 都必须有 $2 \cdot s_{l-1} - s_k \geq 0 \Rightarrow s_k \leq 2 \cdot s_{l-1}$,即 $\max\limits_{l \leq k \leq r}\{ s_k \} \leq 2 \cdot s_{l-1}$。为此当固定 $l$ 后,可以在区间 $[l,n]$ 内二分出最远且合法的右端点 $r$,check 的时候需要快速知道某个区间内 $s_i$ 的最大值,这个可以用 RMQ 或线段树来维护。
最后是满足第 $2$ 个条件。现在我们确定了最远的位置 $r$,但并不是所有位于 $[l,r]$ 区间内的下标都适合作为反转区间的右端点。显然反转区间内的 (
和 )
的数量必须相同,因此合法的右端点 $k \in [l,r]$ 还需要满足 $s_k - s_{l-1} = 0$。所以我们现在需要统计在 $l \leq k \leq r$ 内有多少个位置满足 $s_k = s_{l-1}$,该结果就是以 $l$ 为左端点的合法反转区间数量。方法很简单,只需在预处理 $s_i$ 时开个哈希表存储每个前缀值对应的下标。然后在前缀为 $s_{l-1}$ 的下标中二分出大于等于 $l$ 的最小位置 $x$,以及小于等于 $r$ 的最大位置 $y$,合法的右端点数量就是 $y-x+1$。
AC 代码如下,时间复杂度为 $O(n \log n)$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
char str[N];
int s[N];
int f[18][N];
int query(int l, int r) {
int t = __lg(r - l + 1);
return max(f[t][l], f[t][r - (1 << t) + 1]);
}
void solve() {
scanf("%s", str + 1);
int n = strlen(str + 1);
map<int, vector<int>> mp;
for (int i = 1; i <= n; i++) {
s[i] = s[i - 1] + (str[i] == '(' ? 1 : -1);
mp[s[i]].push_back(i);
}
for (int i = 0; 1 << i <= n; i++) {
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
if (!i) f[i][j] = s[j];
else f[i][j] = max(f[i - 1][j], f[i - 1][j + (1 << i - 1)]);
}
}
LL ret = 0;
for (int i = 1; i <= n; i++) {
int l = i, r = n;
while (l < r) {
int mid = l + r + 1 >> 1;
if (query(i, mid) <= s[i - 1] << 1) l = mid;
else r = mid - 1;
}
if (query(i, l) <= s[i - 1] << 1) {
int x = lower_bound(mp[s[i - 1]].begin(), mp[s[i - 1]].end(), i) - mp[s[i - 1]].begin();
int y = upper_bound(mp[s[i - 1]].begin(), mp[s[i - 1]].end(), l) - mp[s[i - 1]].begin() - 1;
if (x <= y) ret += y - x + 1;
}
}
printf("%lld\n", ret);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
参考资料
Educational Codeforces Round 166 [Rated for Div. 2]:https://codeforces.com/blog/entry/129909
标签:Invertible,sequence,int,leq,Bracket,端点,Sequences,regular,bracket From: https://www.cnblogs.com/onlyblues/p/18224674