https://www.luogu.org/problem/show?pid=3797
题目背景
妖梦是住在白玉楼的半人半灵,拥有使用剑术程度的能力。
题目描述
有一天,妖梦正在练习剑术。地面上摆放了一支非常长的木棒,妖梦把它们切成了等长的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,输出一行一个整数,表示对应询问的答案。
输入输出样例
输入样例#1:
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
考虑了线段树,就不需要考虑形如(((XX)))怎么办
因为线段树都是由底层一步一步合并上去的
sum[k]=sum[l]+sum[r]+(左边最后是(,右边开始是))
维护完整木棒数、区间最后边是否为(,区间最前边是否为)
#include<cstdio> #define N 200001 using namespace std; int sum[N<<2],mid[N<<2],num[N<<2]; int ty[N]; bool left[N<<2],right[N<<2]; struct node { int tot,tot2; bool left,right; node() { left=right=false; tot=tot2=0;} }; void build(int k,int l,int r) { if(l==r) return; mid[k]=l+r>>1; build(k<<1,l,mid[k]); build(k<<1|1,mid[k]+1,r); } void change(int k,int l,int r,int pos,int w) { if(l==r) { ty[pos]=w; left[k]=w==1 ? true : false; right[k]=w==2 ? true : false; if(w) num[k]=1; else num[k]=0; return; } if(pos<=mid[k]) change(k<<1,l,mid[k],pos,w); else change(k<<1|1,mid[k]+1,r,pos,w); if(left[k<<1|1]) left[k]=true; else if(!num[k<<1|1] && left[k<<1]) left[k]=true; else left[k]=false; if(right[k<<1]) right[k]=true; else if(!num[k<<1] && right[k<<1|1]) right[k]=true; else right[k]=false; sum[k]=sum[k<<1]+sum[k<<1|1]+(left[k<<1] & right[k<<1|1]); num[k]=num[k<<1]+num[k<<1|1]; } node merge(node p,node q) { node res; res.tot=p.tot+q.tot+(p.left & q.right); res.tot2=p.tot2+q.tot2; if(q.left) res.left=true; else if(!q.tot2 && p.left) res.left=true; else res.left=false; if(p.right) res.right=true; else if(!p.tot2 && q.right) res.right=true; else res.right=false; return res; } node query(int k,int l,int r,int opl,int opr) { if(l>=opl && r<=opr) { node res; res.left=left[k] ? true : false; res.right=right[k] ? true : false; res.tot=sum[k]; res.tot2=num[k]; return res; } if(opr<=mid[k]) return query(k<<1,l,mid[k],opl,opr); if(opl>mid[k]) return query(k<<1|1,mid[k]+1,r,opl,opr); return merge(query(k<<1,l,mid[k],opl,opr),query(k<<1|1,mid[k]+1,r,opl,opr)); } int main() { int n,m; scanf("%d%d",&n,&m); build(1,1,n); change(1,1,n,1,1); change(1,1,n,n,2); int type,x,y; char c[2]; while(m--) { scanf("%d%d",&type,&x); if(type==1) { scanf("%s",c); if(c[0]=='X') y=0; else if(c[0]=='(') y=1; else y=2; change(1,1,n,x,y); } else scanf("%d",&y),printf("%d ",query(1,1,n,x,y).tot); } }