YbtOJ 「数据结构」第4章 线段树

不想 dp 了怎么办?开个新坑吧。

例题1.求区间和

树状数组不香吗,28行解决(bushi 所以懒得打线段树了。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,c[100005];
int lowbit(int x) {return x&(-x);}
void add(int x,int k){
	for(int i=x;i<=n;i+=lowbit(i)) c[i]+=k;
}
int ask(int x)
{
	int ans=0;
	for(int i=x;i;i-=lowbit(i)) ans+=c[i];
	return ans;
}
int sum(int l,int r){
	return ask(r)-ask(l-1);
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1,k,a,b;i<=m;i++)
	{
		scanf("%lld%lld%lld",&k,&a,&b);
		if(k) cout<<sum(a,b)<<endl;
		else add(a,b);
	}
	return 0;
}

例题2.区间查改

板子。这次老老实实写线段树。现在是 15:10,猜猜我码一遍板子要多久qwq
15:23 好了我码完了,这什么老年人手速啊。今日大聪明行为:N=1e6-5。

code
#include<bits/stdc++.h>
#define int long long
#define ls (now<<1)
#define rs (now<<1|1)
using namespace std;
const int N=1e6+5;
int n,q,a[N],tr[N<<2],lz[N<<2];
void build(int now,int l,int r)
{
	if(l==r) {tr[now]=a[l];return;}
	int mid=(l+r)>>1;
	build(ls,l,mid);build(rs,mid+1,r);
	tr[now]=tr[ls]+tr[rs];
}
void push_down(int now,int l,int r)
{
	int mid=(l+r)>>1;
	tr[ls]+=lz[now]*(mid-l+1);tr[rs]+=lz[now]*(r-mid);
	lz[ls]+=lz[now];lz[rs]+=lz[now];
	lz[now]=0;
}
void modify(int now,int l,int r,int ml,int mr,int k)
{
	if(l==ml&&r==mr)
	{
		tr[now]+=k*(r-l+1); 
		lz[now]+=k; 
		return;
	}
	push_down(now,l,r);
	int mid=(l+r)>>1;
	if(mr<=mid) modify(ls,l,mid,ml,mr,k);
	else if(ml>mid) modify(rs,mid+1,r,ml,mr,k);
	else modify(ls,l,mid,ml,mid,k),modify(rs,mid+1,r,mid+1,mr,k);
	tr[now]=tr[ls]+tr[rs];
}
int query(int now,int l,int r,int ml,int mr)
{
	if(l==ml&&r==mr) return tr[now];
	push_down(now,l,r);
	int mid=(l+r)>>1;
	if(mr<=mid) return query(ls,l,mid,ml,mr);
	else if(ml>mid) return query(rs,mid+1,r,ml,mr);
	else return query(ls,l,mid,ml,mid)+query(rs,mid+1,r,mid+1,mr);
}
signed main()
{
	scanf("%lld%lld",&n,&q);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	build(1,1,n);
	for(int i=1,op,l,r,k;i<=q;i++)
	{
		scanf("%lld%lld%lld",&op,&l,&r);
		if(op==1)
		{
			scanf("%lld",&k);
			modify(1,1,n,l,r,k);
		}
		else cout<<query(1,1,n,l,r)<<endl;
	}
	return 0;
}

例题3.公园遛狗

线段树节点维护区间内最大子段和。
为合并两个区间,还需维护每个区间和,最大前缀和,最大后缀和。

code
#include<bits/stdc++.h>
#define ls (now<<1)

#define rs (now<<1|1) 

using namespace std;
const int N=5e5+5;
int n,q,a[N];
struct node{
	int m,L,R,s;
}tr[N<<2];
void pushup(int now)
{
	tr[now].s=tr[ls].s+tr[rs].s;
	tr[now].m=max(tr[ls].m,tr[rs].m);
	tr[now].m=max(tr[now].m,tr[ls].R+tr[rs].L);
	tr[now].L=max(tr[ls].L,tr[ls].s+tr[rs].L);
	tr[now].R=max(tr[rs].R,tr[rs].s+tr[ls].R);
}
void build(int now,int l,int r)
{
	if(l==r) 
	{
		tr[now].s=tr[now].L=tr[now].R=tr[now].m=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);build(rs,mid+1,r);
	pushup(now);
}
void modify(int now,int l,int r,int x,int k)
{
	if(l==r)
	{
		tr[now].s=tr[now].L=tr[now].R=tr[now].m=k;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) modify(ls,l,mid,x,k);
	else modify(rs,mid+1,r,x,k);
	pushup(now);
}
node query(int now,int l,int r,int ml,int mr)
{
	if(l==ml&&r==mr) return tr[now];
	int mid=(l+r)>>1;
	if(mr<=mid) return query(ls,l,mid,ml,mr);
	else if(ml>mid) return query(rs,mid+1,r,ml,mr);
	else
	{
		node b=query(ls,l,mid,ml,mid),c=query(rs,mid+1,r,mid+1,mr);
		node ans;
		ans.s=b.s+c.s;
		ans.L=max(b.L,b.s+c.L);ans.R=max(c.R,b.R+c.s);
		ans.m=max(b.m,c.m);
		ans.m=max(ans.m,b.R+c.L);
		return ans;
	}
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	build(1,1,n);
	for(int i=1,op,l,r;i<=q;i++)
	{
		scanf("%d%d%d",&op,&l,&r);
		if(op==1) 
		{
			if(l>r) swap(l,r);
			node qwq=query(1,1,n,l,r);
			cout<<qwq.m<<endl;
		}
		else modify(1,1,n,l,r);
	}
	return 0;
}

例题4.维护序列

维护两个懒标记。更新顺序先乘后加。(远古时代的代码

code
#include<bits/stdc++.h>
#define int long long

using namespace std;
int tr[400001];
int n,a[100001],lz1[400001],lz2[400001],m,p1;

void build(int p,int l,int r){
	lz1[p]=1;
	if(l==r)
    {
        tr[p]=a[l];
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    tr[p]=tr[p*2]+tr[p*2+1];
}

void push_down(int p,int l,int r){
	int mid=(l+r)/2;
	if(lz1[p]!=1)
	{
		(lz1[p*2]*=lz1[p]%p1)%=p1;
		(lz1[p*2+1]*=lz1[p]%p1)%=p1;
		(lz2[p*2]*=lz1[p])%=p1;
		(lz2[p*2+1]*=lz1[p])%=p1;
		(tr[p*2]*=(lz1[p])%p1)%=p1;
		(tr[p*2+1]*=(lz1[p])%p1)%=p1;
		lz1[p]=1;
	}
	if(lz2[p]>0)
	{
		(lz2[p*2]+=lz2[p]%p1)%=p1;
		(lz2[p*2+1]+=lz2[p]%p1)%=p1;
		(tr[p*2]+=(lz2[p]*(mid-l+1))%p1)%=p1;
		(tr[p*2+1]+=(lz2[p]*(r-(mid+1)+1))%p1)%=p1;
		lz2[p]=0;
	}
}

void modify1(int p,int l,int r,int ml,int mr,int x){
	if(l==ml&&r==mr)
	{
		(tr[p]*=x%p1)%=p1;
		(lz1[p]*=x%p1)%=p1;
		(lz2[p]*=x%p1)%=p1;
		return;
	}
	int mid=(l+r)/2;
	push_down(p,l,r);
	if(mr<=mid) modify1(p*2,l,mid,ml,mr,x);
	else if(ml>=mid+1) modify1(p*2+1,mid+1,r,ml,mr,x);
	else
	{
		modify1(p*2,l,mid,ml,mid,x);
		modify1(p*2+1,mid+1,r,mid+1,mr,x);
	}
	tr[p]=(tr[p*2]+tr[p*2+1])%p1;
}

void modify2(int p,int l,int r,int ml,int mr,int x){
	if(l==ml&&r==mr)
	{
		tr[p]+=(x*(r-l+1))%p1;
		lz2[p]+=x;
		return;
	}
	int mid=(l+r)/2;
	push_down(p,l,r);
	if(mr<=mid) modify2(p*2,l,mid,ml,mr,x);
	else if(ml>=mid+1) modify2(p*2+1,mid+1,r,ml,mr,x);
	else
	{
		modify2(p*2,l,mid,ml,mid,x);
		modify2(p*2+1,mid+1,r,mid+1,mr,x);
	}
	tr[p]=(tr[p*2]+tr[p*2+1])%p1;
}

int query(int p,int l,int r,int ql,int qr){
    if(l==ql&&qr==r) return tr[p];
    int mid=(l+r)/2;
    push_down(p,l,r);
    if(qr<=mid) return query(p*2,l,mid,ql,qr);
    if(ql>mid) return query(p*2+1,mid+1,r,ql,qr);
    else return (query(p*2,l,mid,ql,mid)+query(p*2+1,mid+1,r,mid+1,qr))%p1;
}

signed main(){
    cin>>n>>p1;
    for(int i=1;i<=n;i++)
    {
    	cin>>a[i];
    }
    cin>>m;
    build(1,1,n);
    for(int i=1,u,x,ql,qr;i<=m;i++)
    {
    	cin>>u;
    	if(u==1) cin>>ql>>qr>>x,modify1(1,1,n,ql,qr,x);
    	else if(u==2) cin>>ql>>qr>>x,modify2(1,1,n,ql,qr,x);
    	else cin>>ql>>qr,cout<<(query(1,1,n,ql,qr))%p1<<endl;
    }
    return 0;
}

例题5.字符串排序

观察到只有 26 种字符。类似桶排,分别统计每个字符数量,排序时区间覆盖。

code
#include<bits/stdc++.h>
#define ls (now<<1)
#define rs (now<<1|1)
using namespace std;
const int N=1e5+5;
int n,m;
char s[N];
int lz[N<<2];
struct node{
	int w[30];
	node(){memset(w,0,sizeof(w));}
}tr[N<<2];
void build(int now,int l,int r)
{
	lz[now]=-1;
	if(l==r)
	{
		tr[now].w[s[l]-'a']=1;
		//cout<<now<<" "<<s[l]-'a'<<endl;
		return;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);build(rs,mid+1,r);
	for(int i=0;i<26;i++) tr[now].w[i]=tr[ls].w[i]+tr[rs].w[i];
	//cout<<now<<" "<<l<<" "<<r<<" "<<tr[now].w[1]<<endl;
}
void push_down(int now,int l,int r)
{
	if(lz[now]==-1) return;
	int k=lz[now],mid=(l+r)>>1;
	for(int i=0;i<26;i++) tr[ls].w[i]=tr[rs].w[i]=0;
	tr[ls].w[k]=mid-l+1;tr[rs].w[k]=r-mid;
	lz[ls]=k,lz[rs]=k;
	lz[now]=-1;
}
void modify(int now,int l,int r,int ml,int mr,int k)
{
	if(l==ml&&r==mr)
	{
		for(int i=0;i<26;i++) tr[now].w[i]=0;
		tr[now].w[k]=(r-l+1);lz[now]=k;
		return;
	}
	push_down(now,l,r);
	int mid=(l+r)>>1;
	if(mr<=mid) modify(ls,l,mid,ml,mr,k);
	else if(ml>mid) modify(rs,mid+1,r,ml,mr,k);
	else modify(ls,l,mid,ml,mid,k),modify(rs,mid+1,r,mid+1,mr,k);
	for(int i=0;i<26;i++) tr[now].w[i]=tr[ls].w[i]+tr[rs].w[i];
}
node query(int now,int l,int r,int ml,int mr)
{
	//cout<<now<<" "<<l<<" "<<r<<endl;
	if(l==ml&&r==mr) 
	{
		//for(int i=1;i<= 
		return tr[now];
	}
	int mid=(l+r)>>1;
	push_down(now,l,r);
	if(mr<=mid) return query(ls,l,mid,ml,mr);
	else if(ml>mid) return query(rs,mid+1,r,ml,mr);
	else
	{
		node c=query(ls,l,mid,ml,mid),d=query(rs,mid+1,r,mid+1,mr);
		node ans;
		for(int i=0;i<26;i++) ans.w[i]=c.w[i]+d.w[i];
		return ans;
	}
}
void s0rt(int op,int l,int r)
{
	node qwq=query(1,1,n,l,r);
	if(op)
	{
		int now=l;
		for(int i=0;i<26;i++)
		{
			if(!qwq.w[i]) continue;
			//cout<<i<<endl;
			modify(1,1,n,now,now+qwq.w[i]-1,i);
			now+=qwq.w[i];
		}
	}
	else
	{
		int now=l;
		for(int i=25;i>=0;i--)
		{
			if(!qwq.w[i]) continue;
			modify(1,1,n,now,now+qwq.w[i]-1,i);
			now+=qwq.w[i];
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	scanf("%s",s+1);
	build(1,1,n);
	//for(int i=0;i<4;i++) cout<<tr[2].w[i]<<endl; 
	for(int i=1,op,l,r;i<=m;i++)
	{
		scanf("%d%d%d",&l,&r,&op);
		s0rt(op,l,r); 
	}
	for(int i=1;i<=n;i++)
	{
		node qwq=query(1,1,n,i,i);
		for(int j=0;j<26;j++) if(qwq.w[j]) putchar(j+'a');
	}
	//node qwq=query(1,1,n,1,3);
	//for(int i=0;i<4;i++) cout<<qwq.w[i]<<endl;
	return 0;
}
/*
5 2 
cabcd 
1 3 1 
3 5 0
*/ 

1.取模问题

维护区间和和区间最大值。最大值小于模数就直接 return 掉。
其余暴力取模。每次取模数值至少减半,故取模次数不会太多。

code
#include<bits/stdc++.h>
#define int long long
#define ls (now<<1)
#define rs (now<<1|1)
using namespace std;
const int N=1e5+5;
int n,m,a[N];
int maxn[N<<2],tr[N<<2];
void pushup(int now)
{
	maxn[now]=max(maxn[ls],maxn[rs]);
	tr[now]=tr[ls]+tr[rs];
}
void build(int now,int l,int r)
{
	if(l==r)
	{
		maxn[now]=tr[now]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);build(rs,mid+1,r); 
	pushup(now);
}
void modify(int now,int l,int r,int x,int k)
{
	if(l==r)
	{
		maxn[now]=tr[now]=k;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) modify(ls,l,mid,x,k);
	else modify(rs,mid+1,r,x,k);
	pushup(now);
}
int query(int now,int l,int r,int ml,int mr)
{
	if(l==ml&&r==mr) return tr[now];
	int mid=(l+r)>>1;
	if(mr<=mid) return query(ls,l,mid,ml,mr);
	else if(ml>mid) return query(rs,mid+1,r,ml,mr);
	else return query(ls,l,mid,ml,mid)+query(rs,mid+1,r,mid+1,mr);
}
void mod(int now,int l,int r,int ml,int mr,int x)
{
	int mid=(l+r)>>1; 
	if(l==ml&&r==mr)
	{
		if(maxn[now]<x) return;
		if(l==r)
		{
			tr[now]%=x;maxn[now]%=x;
			return;
		}
		mod(ls,l,mid,ml,mid,x);mod(rs,mid+1,r,mid+1,mr,x);
		pushup(now);
		return;
	}
	if(mr<=mid) mod(ls,l,mid,ml,mr,x);
	else if(ml>mid) mod(rs,mid+1,r,ml,mr,x);
	else mod(ls,l,mid,ml,mid,x),mod(rs,mid+1,r,mid+1,mr,x);
	pushup(now);
}
signed main()
{
	scanf("%lld%lld",&n,&m); 
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	build(1,1,n);
	for(int i=1,op,l,r,x;i<=m;i++)
	{
		scanf("%lld%lld%lld",&op,&l,&r);
		if(op==1) cout<<query(1,1,n,l,r)<<endl;
		else if(op==2) scanf("%lld",&x),mod(1,1,n,l,r,x);
		else modify(1,1,n,l,r);
	}
	return 0;
}

2.魔法传输

差分,则修改操作转换为区间加,单点查询转化为求前缀和。
然后就变成裸的板子了。

code
#include<bits/stdc++.h>
#define int long long
#define ls (now<<1)
#define rs (now<<1|1)
using namespace std;
const int N=1e5+5;
const int mod=1e9+7; 
int n,q,a[N],tr[N<<2],lz[N<<2];
void push_down(int now,int l,int r)
{
	int mid=(l+r)>>1;
	tr[ls]=(tr[ls]+lz[now]*(mid-l+1))%mod;tr[rs]=(tr[rs]+lz[now]*(r-mid))%mod;
	lz[ls]=(lz[ls]+lz[now])%mod;lz[rs]=(lz[rs]+lz[now])%mod;
	lz[now]=0;
}
void modify(int now,int l,int r,int ml,int mr,int k)
{
	if(ml>mr||mr>n||ml<1) return;
	if(l==ml&&r==mr)
	{
		tr[now]=(tr[now]+k*(r-l+1))%mod; 
		lz[now]=(lz[now]+k)%mod; 
		return;
	}
	push_down(now,l,r);
	int mid=(l+r)>>1;
	if(mr<=mid) modify(ls,l,mid,ml,mr,k);
	else if(ml>mid) modify(rs,mid+1,r,ml,mr,k);
	else modify(ls,l,mid,ml,mid,k),modify(rs,mid+1,r,mid+1,mr,k);
	tr[now]=(tr[ls]+tr[rs])%mod;
}
int query(int now,int l,int r,int ml,int mr)
{
	if(l==ml&&r==mr) return tr[now];
	push_down(now,l,r);
	int mid=(l+r)>>1;
	if(mr<=mid) return query(ls,l,mid,ml,mr);
	else if(ml>mid) return query(rs,mid+1,r,ml,mr);
	else return (query(ls,l,mid,ml,mid)+query(rs,mid+1,r,mid+1,mr))%mod;
}
signed main()
{
	scanf("%lld%lld",&n,&q);
	for(int i=1;i<=q;i++)
	{
		char c;c=getchar();
		while(c!='C'&&c!='Q') c=getchar();
		if(c=='C') 
		{
			int l,r;
			scanf("%lld%lld",&l,&r);
			modify(1,1,n,l,r,1);
			modify(1,1,n,r+1,r+1,-(r-l+1));
		}
		else
		{
			int x;
			scanf("%lld",&x);
			cout<<query(1,1,n,1,x)%mod<<endl;
		}
	}
	return 0;
}

3.队伍整理

喜提全网首A/cy
不用离散化不用链表不用权值线段树(那篇题解实在离谱)。其实这题不难,不要被网上题解劝退了qwq
把题分成两部分来看。
询问前面成绩最好的同学:这就是问前面人排名的最小值。那把没有人的位置都单点赋值成 \(inf\),然后线段树维护区间最小值即可。
最少移动多少个人:开一个数组记录每个位置是否有人。枚举每个长度为 \(n\) 的区间,通过前面那个数组的区间和判断这个区间有几个空位。移动时,我们把不在该区间的人都移进来。所以,空位最少的区间的空位数就是答案。这个东西在最后 O(n) 扫一下就好了。
实现细节:开一个 map 来记录每个排名的人所在的位置,省去了离散化的麻烦。总体写起来码量不大,80 行左右。
坑点:注意查询结果是 inf 时也要判作无解。
不知道讲清楚了没有/wq
upd:抱歉之前做麻烦了,不需要树状数组,已经更新了题解和代码。h2s 是神!

code
#include<bits/stdc++.h>
#define ls (now<<1)
#define rs (now<<1|1)
using namespace std;
const int inf=2e9;
const int N=2e5+5;
int n,m,a[N];
map<int,int> w;
int tr[N<<2],cnt;
//线段树 
void build(int now,int l,int r)
{
	if(l==r)
	{
		tr[now]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);build(rs,mid+1,r);
	tr[now]=min(tr[ls],tr[rs]);
}
void modify(int now,int l,int r,int x,int k)
{
	if(l==r)
	{
		tr[now]=k;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) modify(ls,l,mid,x,k);
	else modify(rs,mid+1,r,x,k);
	tr[now]=min(tr[ls],tr[rs]);
}
int query(int now,int l,int r,int ml,int mr)
{
	if(mr<ml) return -1;
	if(l==ml&&r==mr) return tr[now];
	int mid=(l+r)>>1;
	if(mr<=mid) return query(ls,l,mid,ml,mr);
	else if(ml>mid) return query(rs,mid+1,r,ml,mr);
	else return min(query(ls,l,mid,ml,mid),query(rs,mid+1,r,mid+1,mr));
}
int ct[N];
int main()
{
	scanf("%d%d",&n,&m);cnt=n;
	for(int i=1;i<=n+m;i++) a[i]=inf;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),w[a[i]]=i,ct[i]=1;
	//for(int i=1;i<=n;i++) cout<<sum(i,i)<<" ";
	build(1,1,n+m);
	for(int i=1;i<=m;i++)
	{
		char c;int x;
		c=getchar();
		while(c!='A'&&c!='M') c=getchar();
		scanf("%d",&x);
		if(c=='A')
		{
			if(!w[x]) cout<<"-1"<<endl;
		 	else if(query(1,1,n+m,1,w[x]-1)==inf) cout<<"-1"<<endl;
			else cout<<query(1,1,n+m,1,w[x]-1)<<endl;
		}
		else
		{
			modify(1,1,n+m,w[x],inf);
			modify(1,1,n+m,++cnt,x);
			ct[(int)w[x]]=0;ct[cnt]=1;w[x]=cnt;
		}
	}
	int maxn=0,sum=0;
	for(int i=1;i<n;i++) sum+=ct[i];
	for(int i=1;i<=cnt-n+1;i++)
	{
		sum-=ct[i-1];sum+=ct[i+n-1];
		maxn=max(maxn,sum);
	}
	cout<<n-maxn<<endl; 
	return 0;
}

4.和或异或

线段树维护答案,pushup 时根据深度判断一下是或还是异或。

code
#include<bits/stdc++.h>
#define int long long
#define ls (now<<1)
#define rs (now<<1|1)
using namespace std;
const int N=1.4e5+5;
int n,q,qwq;
int tr[N<<2],a[N];
void pushup(int now,int dep)
{
	if((qwq&1)^(dep&1)) 
	{
		tr[now]=tr[ls]^tr[rs];
		//cout<<now<<" "<<tr[now]<<" "<<tr[ls]<<" "<<tr[rs]<<" 1"<<endl;
	}
	else 
	{
		tr[now]=tr[ls]|tr[rs];
		//cout<<now<<" "<<tr[now]<<" "<<tr[ls]<<" "<<tr[rs]<<" 2"<<endl;
	}
}
void build(int now,int l,int r,int dep)
{
	//cout<<now<<" "<<l<<" "<<r<<endl;
	if(l==r) {tr[now]=a[l];return;}
	int mid=(l+r)>>1;
	build(ls,l,mid,dep+1);build(rs,mid+1,r,dep+1);	
	pushup(now,dep);
}
void modify(int now,int l,int r,int x,int k,int dep)
{
	if(l==r) {tr[now]=k;return;}
	int mid=(l+r)>>1;
	if(x<=mid) modify(ls,l,mid,x,k,dep+1);
	else modify(rs,mid+1,r,x,k,dep+1);
	pushup(now,dep); 
}
signed main()
{
	scanf("%lld%lld",&n,&q);
	qwq=n;n=pow(2,n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	build(1,1,n,1);
	for(int i=1,x,y;i<=q;i++)
	{
		scanf("%lld%lld",&x,&y);
		modify(1,1,n,x,y,1);
		cout<<tr[1]<<endl;
	}
	return 0;
}

5.括号匹配

维护区间内已匹配括号的数量、多余左括号的数量、多余右括号的数量。
为什么线段树这章的题都这么板啊qaq

code
#include<bits/stdc++.h>
#define ls (now<<1)
#define rs (now<<1|1)
using namespace std;
const int N=4e5+5;
int n,m;
char s[N];
struct node{
	int L,R,w;
}tr[N<<2];
void pushup(int now)
{
	int qwq=min(tr[ls].L,tr[rs].R);
	tr[now].w=tr[ls].w+tr[rs].w+2*qwq;
	tr[now].L=tr[ls].L+tr[rs].L-qwq;
	tr[now].R=tr[ls].R+tr[rs].R-qwq;
}
void build(int now,int l,int r)
{
	if(l==r)
	{
		if(s[l]=='(') tr[now].L=1;
		else tr[now].R=1;
		return;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);build(rs,mid+1,r);
	pushup(now);
}
node query(int now,int l,int r,int ml,int mr)
{
	if(l==ml&&r==mr) return tr[now];
	int mid=(l+r)>>1;
	if(mr<=mid) return query(ls,l,mid,ml,mr);
	else if(ml>mid) return query(rs,mid+1,r,ml,mr);
	else 
	{
		node ll=query(ls,l,mid,ml,mid),rr=query(rs,mid+1,r,mid+1,mr); 
		int qwq=min(ll.L,rr.R);
		node ans;
		ans.w=ll.w+rr.w+2*qwq;
		ans.L=ll.L+rr.L-qwq;
		ans.R=ll.R+rr.R-qwq;
		return ans;
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	scanf("%s",s+1);
	build(1,1,n);
	for(int i=1,l,r;i<=m;i++)
	{
		scanf("%d%d",&l,&r);
		node qaq=query(1,1,n,l,r);
		cout<<qaq.w<<endl;
	}
	return 0;
}
posted @ 2022-08-16 15:11  樱雪喵  阅读(78)  评论(0编辑  收藏  举报