数据结构大师
时间限制: 1 Sec 内存限制: 128 MB
题目描述
小(Z)是个数据结构高手,这天他得到了一个由左括号和右括号组成的字符串。随之而来的是 (m) 次询问,对于第 (i) 次询问,小Z需要回答出这个字符串的第(l_i) 到(r_i) 个字符组成的字串中最长的合法括号子序列的长度。
小(Z)认为一个由左右括号组成的序列(A)合法,当且仅当其满足至少一个以下条件。
(A)为空。
- (A=(B))其中(B)是一个合法的括号序列。
- (A=BC),其中(BC)都是合法的括号序列。
- 比如合法的括号序列有((),()(),(()))等。
输入
第一行读入两个数字(n,m),分别表示长度和询问次数,接下来一行读入字符串(S)。
最后m行每行读入两个数(l_i,r_i),表示这次询问的区间。
输出
对于每个询问输出一行表示答案。
样例输入
4 1
(())
2 4
样例输出
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)的维护显然,减去已经匹配成功的即可;
代码:
//#pragma GCC optimize(2)
//#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int N=1e6+5;
const ll mod=1e9+7;
const double eps=1e-5;
//const double pi=acos(-1);
#define ls p<<1
#define rs p<<1|1
char s[N];
struct seg
{
int l,r,mx;
}t[N<<2];
void build(int p,int l,int r)
{
if(l==r)
{
t[p].l=s[l]=='(';
t[p].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[p].mx=t[ls].mx+x+t[rs].mx;
t[p].l=t[ls].l-x+t[rs].l;
t[p].r=t[ls].r+t[rs].r-x;
}
seg ask(int p,int l,int r,int x,int y)
{
if(x<=l&&r<=y)
return t[p];
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
{
seg ans;
seg lx=ask(ls,l,mid,x,y);
seg rx=ask(rs,mid+1,r,x,y);
int use=min(lx.l,rx.r);
ans.mx=lx.mx+rx.mx+use;
ans.l=lx.l+rx.l-use;
ans.r=lx.r+rx.r-use;
return ans;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
int n,m;
cin>>n>>m;
cin>>(s+1);
build(1,1,n);
while(m--)
{
int l,r;
cin>>l>>r;
printf("%d
",ask(1,1,n,l,r).mx*2);
}
return 0;
}