【题解报告】P3797 妖梦斩木棒

前置芝士:线段树 (应该会的吧)

本题可以算线段树的基本操作了,进入正题吧:

我们线段树中存储三个变量:

  • (sum[x]) :完整的木棒的数量

  • (ls[x]) :最右边的括号是否是 (

  • (rs[x]) :最左边的括号是否是 )

做题过程分为建树,单点修改和查询 (很经典了吧)

本题在建树和单点修改时只有一点与其他节点不同,就是合并节点信息,在这里略作解释:

首先是最简单直接赋值:

(rs_x=rs_{sonL},ls_x=ls_{sonR})

(sum_x=sum_{sonL}+sum_{sonR}+[ls_{sonL}==1&&rs_{sonR}==1])

另外注意一点,当(sonL)的区集中全是(X)时,(rs_x)是要等于(rs_{sonR})的,而且易证得,如果(sum_{sonL}==0&&rs_{sonL}==0&&ls_{sonL}==0),则可以说明左儿子全是(X),因此合并部分代码如下:

inline void update(int x)
{
	rs[x]=rs[x<<1],ls[x]=ls[x<<1|1];
	if(sum[x<<1]==0&&ls[x<<1]==0&&rs[x<<1]==0) rs[x]=rs[x<<1|1];
	if(sum[x<<1|1]==0&&ls[x<<1|1]==0&&rs[x<<1|1]==0) ls[x]=ls[x<<1];
	sum[x]=sum[x<<1]+sum[x<<1|1]+(ls[x<<1]+rs[x<<1|1]>>1);
}

再就是查询了,这里需要用到一个小技巧,线段树的遍历顺序是从左往右的,所以我们每遇到一个合法区间,和前一个遇到的合法区间合并计算答案就好了,同样需要注意全为(X)的情况。

(OVER)~~

完整代码如下:

#include<bits/stdc++.h>
#define re register
using namespace std;
inline int read()
{
	re int x=0,f=1;
	re char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') f*=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return x*f;
}
const int N=200005;
int n,m,sum[N<<2],ans;
bool ls[N<<2],rs[N<<2],last;
char ch[N],s[1];
inline void update(int x)
{
	rs[x]=rs[x<<1],ls[x]=ls[x<<1|1];
	if(sum[x<<1]==0&&ls[x<<1]==0&&rs[x<<1]==0) rs[x]=rs[x<<1|1];
	if(sum[x<<1|1]==0&&ls[x<<1|1]==0&&rs[x<<1|1]==0) ls[x]=ls[x<<1];
	sum[x]=sum[x<<1]+sum[x<<1|1]+(ls[x<<1]+rs[x<<1|1]>>1);
}
void build(int l,int r,int x)
{
	if(l==r) {ls[x]=(ch[l]=='('),rs[x]=(ch[l]==')');return ;}
	re int mid=l+r>>1;
	build(l,mid,x<<1),build(mid+1,r,x<<1|1);
	update(x);
}
void change(int l,int r,int x,int p,char a)
{
	if(l==r) {ls[x]=(a=='('),rs[x]=(a==')');return ;}
	re int mid=l+r>>1;
	if(p<=mid) change(l,mid,x<<1,p,a);
	else change(mid+1,r,x<<1|1,p,a);
	update(x);
}
void ask(int l,int r,int x,int L,int R)
{
	if(L<=l&&r<=R)
	{
		if(sum[x]==0&&ls[x]==0&&rs[x]==0) return ;
		ans+=sum[x]+(last+rs[x]>>1);
		last=ls[x];//last为上一个合法区间最右边一个括号是否为 (
		return ;
	}
	re int mid=l+r>>1;
	if(L<=mid) ask(l,mid,x<<1,L,R);
	if(R>mid) ask(mid+1,r,x<<1|1,L,R);
}
inline void Fre()
{
	freopen("P3797.in","r",stdin);
	freopen("P3797.out","w",stdout);
}
int main()
{
	Fre();
	n=read(),m=read();
	for(re int i=2;i<n;i++) ch[i]='X';ch[1]='(',ch[n]=')';
	build(1,n,1);
	for(re int i=1,type,x,y;i<=m;i++)
	{
		type=read(),x=read();
		if(type==1)
		{
			scanf("%s",s);
			change(1,n,1,x,s[0]);
		}
		else
		{
			y=read(),ask(1,n,1,x,y);
			printf("%d
",ans);
			ans=last=0;
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/jkzcr01-QAQ/p/13847508.html