D. Invertible Bracket Sequences

onlyblues發表於2024-05-31

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. 在任意一個字首中 ( 的數量不小於 ) 的數量。
  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

相關文章