【題解報告】P3797 妖夢斬木棒
阿新 • • 發佈:2020-10-20
前置芝士:線段樹 (應該會的吧)
本題可以算線段樹的基本操作了,進入正題吧:
我們線段樹中儲存三個變數:
-
\(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; }