E - Infinite Parenthesis Sequence
基本信息
题解
考虑计算 表示 中第 个左括号的下标( 可以为 甚至负数),设 表示满足 的最大的 ,则区间 里左括号的总数即为 。 的值通过二分 即可算出。
另外,由于我们只关心 的差值,因此第 个左括号具体对应哪个括号其实可以随便指定。方便起见,把最开始的有限字符串中的第一个左括号选为第 个左括号。
接下来考虑 的转移方程。
- 如果 的下一个字符是右括号,因为
() -> )(
,有 。
- 如果 的下一个字符是左括号,因为
(( -> (*
,有 。
综合起来,转移方程为
假设 是从 转移过来的,那么第一维从 走到 的过程中, 中的第二项恰选了 次,第一项恰选了 次,因此转移方程还可以写为
容易看出决策区间的长度是 。设最开始的有限字符串中,左括号的数量共有 个。首先分析 的时候如何求出最小值。
设 。注意到 ,有
其中 是任意非负数。因此我们可以用 RMQ 预处理 到 ,然后把 映射到 求最小值即可。
接下来分析 比较大时的解法。讨论 的值:
- 若 ,说明 ,因此最小值只存在于 这个范围中。
- 若 ,说明 ,因此最小值只存在于 这个范围中。
这样我们就把区间长度缩小到了 ,又可以通过 RMQ 求出最小值了。
上面还有一个细节问题没有解决:二分 的时候,上下界是多少?我们来看 的取值范围。
- 取 时,有 ,因此 的最小值可能是 。
- 由于 ,,因此 ,则 的最大值可能是 。
所以二分上下界取 即可。
复杂度 。
参考代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 | #include <bits/stdc++.h>
#define MAXN ((int) 1e5)
#define MAXP 20
using namespace std;
int n, m, q;
char s[MAXN + 10];
int lg[MAXN + 10], rmq[MAXP][MAXN * 2 + 10];
// RMQ 预处理 F(0) 到 F(2m - 1)
void preRmq() {
m = 0;
for (int i = 0; i < n; i++) if (s[i] == '(') rmq[0][m++] = i;
for (int i = m; i < m * 2; i++) rmq[0][i] = rmq[0][i - m] + n;
for (int i = 0; i < m * 2; i++) rmq[0][i] -= i * 2;
for (int p = 1; p < MAXP; p++) for (int i = 0, ii = (1 << p) - 1; ii < m * 2; i++, ii++) {
int j = i + (1 << (p - 1));
rmq[p][i] = min(rmq[p - 1][i], rmq[p - 1][j]);
}
}
long long calc(long long K, long long i) {
long long L, R;
// 根据 n - 2m 的值,将区间长度缩小到 min(m, k)
if (m * 2 <= n) {
L = i;
R = min(i + K, L + m - 1);
} else {
R = i + K;
L = max(i, R - m + 1);
}
// 将区间 [L, R] 映射到 [L mod m, L mod m + R - L],再用 RMQ 求最小值
long long div = (L >= 0 ? L / m : (L + 1) / m - 1);
int mod = (L % m + m) % m;
long long delta = n * div - 2 * div * m;
int len = R - L + 1, p = lg[len];
return min(rmq[p][mod], rmq[p][mod + len - (1 << p)]) + delta + K + 2 * i;
}
// 二分求 g(K, lim) 的值
long long query(long long K, long long lim) {
long long head = -3e9, tail = 3e9;
while (head < tail) {
long long mid = (head + tail + 1) >> 1;
if (calc(K, mid) <= lim) head = mid;
else tail = mid - 1;
}
return head;
}
void solve() {
scanf("%s", s); n = strlen(s);
preRmq();
scanf("%d", &q);
while (q--) {
long long K, l, r; scanf("%lld%lld%lld", &K, &l, &r);
// 特殊情况:没有左括号
if (m == 0) printf("0\n");
else printf("%lld\n", query(K, r) - query(K, l - 1));
}
}
int main() {
lg[1] = 0;
for (int i = 2; i <= MAXN; i++) lg[i] = lg[i >> 1] + 1;
int tcase; scanf("%d", &tcase);
while (tcase--) solve();
return 0;
}
|