1. 程式人生 > 實用技巧 >【題解報告】P3797 妖夢斬木棒

【題解報告】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\n",ans);
			ans=last=0;
		}
	}
	return 0;
}