数据结构大师

数据结构大师

时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]

题目描述

小Z是个数据结构高手,这天他得到了一个由左括号和右括号组成的字符串。随之而来的是m次询问,对于第i次询问,小Z需要回答出这个字符串的第li到ri个字符组成的字串中最长的合法括号子序列的长度。
小Z认为一个由左右括号组成的序列A合法,当且仅当其满足至少一个以下条件。
·A为空。
·A=(B)其中B是一个合法的括号序列。
·A=BC,其中BC都是合法的括号序列。
比如合法的括号序列有(),()(),(())等。

输入

第一行读入两个数字n,m,分别表示长度和询问次数,接下来一行读入字符串S。
最后m行每行读入两个数li,ri,表示这次询问的区间。

输出

对于每个询问输出一行表示答案。

样例输入 Copy

4 1
(())
2 4

样例输出 Copy

2

提示

对于30%的数据,满足n,m<=500。
对于60%的数据,满足n,m<=5000。
对于100%的数据,满足1≤n≤\(10^6\),1≤m≤\(10^5\)

思路

想到是区间问题自然想到了线段树,由于子序列不连续,我们用线段树维护'('和')'的数量记为l,r。
那么合并ls和rs新增匹配min(l,r),设为x,(若记mx为区间最长匹配对数)也即t[p].mx+=x;
同时我们累和ls,rs的长度,最终t[p].mx=t[ls].mx+t[rs].mx+x;
对l,r的维护显然,减去已经匹配成功的即可;

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
char s[maxn];
#define ls (rt<<1)
#define rs ((rt<<1)+1)
int n,m;
struct node {
    node() {};

    node(int l, int r, int mx) : l(l), r(r), mx(mx) {};
    int l, r, mx;
}t[maxn<<2];
void build(int rt,int l,int r) {
    if (l == r) {
        t[rt].l = s[l] == '(';
        t[rt].r = s[l] == ')';
        return;
    }
    int mid = l + r >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    int x = min(t[ls].l, t[rs].r);
    t[rt].mx = t[ls].mx + x + t[rs].mx;
    t[rt].l = t[ls].l - x + t[rs].l;
    t[rt].r = t[ls].r + t[rs].r - x;
}
node ask(int rt,int l,int r,int x,int y) {
    if (x <= l && r <= y)
        return t[rt];
    int mid = l + r >> 1;
    if (y <= mid)
        return ask(ls, l, mid, x, y);
    else if (x > mid)
        return ask(rs, mid + 1, r, x, y);
    else {
        node lx = ask(ls, l, mid, x, y);
        node rx = ask(rs, mid + 1, r, x, y);
        int use = min(lx.l, rx.r);
        return node(lx.l + rx.l - use, lx.r + rx.r - use, lx.mx + rx.mx + use);
    }
}
int main() {
    scanf("%d%d", &n, &m);
    scanf("%s", s + 1);
    build(1, 1, n);
    while (m--) {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", ask(1, 1, n, l, r).mx * 2);
    }
    return 0;
}
posted @ 2020-04-13 08:06  Snow_in_winer  阅读(134)  评论(0编辑  收藏  举报