天天看点

CF380C Sereja and Brackets 题解 数列分块

题目链接:​​https://codeforces.com/contest/380/problem/C​​

题目大意:给定长度为 \(n(\le 10^6)\) 的一个括号序列,有 \(m(\le 10^5)\) 次询问,每次询问给定一个区间 \([l,r]\),你需要回答出区间 \([l,r]\)

解题思路:

首先,无论在哪个区间范围内,每一个 ')' 所匹配的 '(' 都是固定的。

我们可以用 \(f[i]\) 表示下标为 \(i\)

对于每次询问的 \(l\) 和 \(r\),问题就变成了询问区间 \([l,r]\) 范围内存在多少 \(f[i]\) 满足 \(f[i] \ge l\)。

这个问题用 分块 是很好解决的,对于完整的分开,只需要初始时对每个分块的 \(f[i]\) 排序,然后查询的时候就可以二分查找 \(\le l\)

时间复杂度是 \(O(m \cdot \sqrt{n} \cdot \log n)\),但是因为常数比较小(这里的 \(\log n\) 其实是 \(\log \sqrt{n} = \frac{1}{2} \log n\),然后不匹配的状态 \(f[i]\)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
char s[maxn];
int n, m, l, r, f[maxn];
int blo, bl[maxn];
vector<int> vec[1010];

void init() {
    stack<int> stk;
    for (int i = 1; i <= n; i++) {
        if (s[i] == '(') stk.push(i);
        else if (!stk.empty()) {
            f[i] = stk.top();
            stk.pop();
        }
    }
    blo = sqrt(n);
    for (int i = 1; i <= n; i++) {
        bl[i] = (i - 1) / blo + 1;
        if (f[i]) vec[bl[i]].push_back(f[i]);
    }
    for (int i = 1; i <= bl[n]; i++)
        sort(vec[i].begin(), vec[i].end());
}

int solve(int l, int r) {
    int cnt = 0;
    for (int i = l; i <= min(bl[l]*blo, r); i++) if (f[i] >= l) cnt++;
    if (bl[l] != bl[r]) {
        for (int i = (bl[r]-1)*blo+1; i <= r; i++) if (f[i] >= l) cnt++;
    }
    for (int i = bl[l]+1; i < bl[r]; i++) {
        int num = vec[i].end() - lower_bound(vec[i].begin(), vec[i].end(), l);
        cnt += num;
    }
    return cnt * 2;
}

int main() {
    scanf("%s%d", s+1, &m);
    n = strlen(s+1);
    init();
    while (m--) {
        scanf("%d%d", &l, &r);
        printf("%d\n", solve(l, r));
    }
    return 0;
}