数据结构大师
时间限制: 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
", ask(1, 1, n, l, r).mx * 2);
}
return 0;
}