妖夢斬木棒
題目背景
妖夢是住在白玉樓的半人半靈,擁有使用劍術程度的能力。
題目描述
有一天,妖夢正在練習劍術。地面上擺放了一支非常長的木棒,妖夢把它們切成了等長的n段。現在這個木棒可以看做由三種小段構成,中間的n-2段都是左右都被切斷的斷頭,我們記做’X’,最左邊的一段和最右邊的一段各有一個圓頭,記做’(‘和’)’。幽幽子吃飽後閑來無事,決定戲弄一下妖夢。她拿來了許多這樣的三種小段木棒,來替換掉妖夢切下來的n段中的一部分,然後問妖夢一些問題。這些操作可以這樣描述:
1 x C 將第x個小段的木棒替換成C型,C只會是’X’,’(‘,’)’中的一種
2 l r 詢問妖夢從第l段到第r段之間(含l,r),有多少個完整的木棒
完整的木棒左右兩端必須分別為’(‘和’)’,並且中間要麽什麽都沒有,要麽只能有’X’。
雖然妖夢能夠數清楚這些問題,但幽幽子覺得她回答得太慢了,你能教給妖夢一個更快的辦法嗎?
輸入輸出格式
輸入格式:
第一行兩個整數n,m,n表示共有n段木棒,m表示有m次操作。
木棒的初始形狀為(XXXXXX......XXXXXX)。
接下來m行,每行三個整數/字符,用空格隔開。第一個整數為1或2,表示操作的類型,若類型為1,則接下來一個整數x,一個字符C。若類型為2,接下來兩個整數l,r。含義見題目描述。
輸出格式:
對於每一個操作2,輸出一行一個整數,表示對應詢問的答案。
輸入輸出樣例
4 4 2 1 4 2 2 4 1 2 ( 2 2 4輸出樣例#1:
1 0 1
說明
對於30%的數據,1<=n,m<=1000
對於100%的數據,1<=n,m<=200000
by-orangebird
線段樹
對於每一個區間,維護是否向左開c[rt].ls(右括號左邊無木棍),是否向右開c[rt].rs(左括號右邊無棍)
再維護是否為空c[rt].ps(用處:合並時,c[rt*2].ps&&c[rt*2+1].ls→c[rt].ls=1)
還維護完整木棍數才c[rt].sum;合並時c[rt*2].rs&&c[rt*2+1].ls→c[rt].sum=c[rt*2].sum+c[rt*2+1].sum+1;
註意查詢時,不能直接返回c[rt].sum並求和。因為會多算或少算。要返回所有維護信息,再把解合並
如查詢1-7(紅色為返回c[rt].sum的節點),ans=1,但明顯ans應等於2,應為返回時忽略了左右單獨括號的合並
(xx)()(xx)
(xx)( )(xx)
)(x x)
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; struct Messi { int ls,rs,sum,ps; }c[800001]; int n,m; Messi merge(Messi a,Messi b) { Messi x; x.ps=a.ps&b.ps; x.ls=a.ps?b.ls:a.ls; x.rs=b.ps?a.rs:b.rs; x.sum=a.sum+b.sum; if (b.ls&&a.rs&&!b.ps&&!a.ps) x.sum++; return x; } void build(int rt,int l,int r) { if (l==r) { if (l==1) { c[rt].rs=1; c[rt].ps=0; c[rt].ls=0; } else if (r==n) { c[rt].rs=0; c[rt].ps=0; c[rt].ls=1; } else { c[rt].rs=1; c[rt].ls=1; c[rt].ps=1; } return; } int mid=(l+r)/2; build(rt*2,l,mid); build(rt*2+1,mid+1,r); c[rt]=merge(c[rt*2],c[rt*2+1]); } void update(int rt,int l,int r,int x,int sign) { if (l==r) { if (sign==1) { c[rt].rs=1; c[rt].ps=0; c[rt].ls=0; } else if (sign==0) { c[rt].rs=0; c[rt].ps=0; c[rt].ls=1; } else { c[rt].rs=1; c[rt].ls=1; c[rt].ps=1; } return; } int mid=(l+r)/2; if (x<=mid) update(rt*2,l,mid,x,sign); else update(rt*2+1,mid+1,r,x,sign); c[rt]=merge(c[rt*2],c[rt*2+1]); } Messi query(int rt,int l,int r,int L,int R) { if (l>=L&&r<=R) { return c[rt]; } int mid=(l+r)/2; if (R<=mid) return query(rt*2,l,mid,L,R); if (L>mid) return query(rt*2+1,mid+1,r,L,R); return merge(query(rt*2,l,mid,L,R),query(rt*2+1,mid+1,r,L,R)); } int main() {int i,j,k,x,y; char ch; cin>>n>>m; build(1,1,n); for (i=1;i<=m;i++) { scanf("%d",&k); if (k==2) { scanf("%d%d",&x,&y); //cout<<‘p‘<<x<<‘ ‘<<y<<endl; printf("%d\n",query(1,1,n,x,y).sum); } else { int sign; scanf("%d %c",&x,&ch); //cout<<‘q‘<<x<<‘ ‘<<ch<<endl; if (ch==‘(‘) sign=1; else if (ch==‘)‘)sign=0; else sign=2; update(1,1,n,x,sign); } } }
妖夢斬木棒