[HNOI2011]括号修复
【出处】
HNOI2011Day1
【题目描述】
给定一个长度为N的由“(”和“)”组成的字符串和M个操作。
要求实现四个操作:
1、统一:将[a,b]之间的所有括号改成c(“(”或“)”)
2、翻转:将[a,b]之间的字符串翻转
3、改变:将[a,b]之间的‘(’变成‘)’
4、询问:询问[a,b]之间的字符串至少要改变多少位才能变成合法的括号序列。
N,M≤100000
【算法分析】
此题非常巧妙,可以成功转化成类似最大连续子序列的求解方式,并运用Splay进行维护。
对于一个已知的括号,我们该如何求出询问的答案?
比如)((())((这样一个,我们把符合的一层层去掉,就变成了)(((,那么很显然答案是3。并且易得,所有的字符串化简后必是))...)((...(的形式(或全是)或(),答案就是。关键就是如何求出个数来。考虑左起的),我们发现,当一个字符串)))))())中有了一个(,就相当于连续的(减少了1。如果我们把(看成+1,)看成-1,那么答案就是从最左边开始连续最小的一段。同样的从右边可算出(。即我们把原串的(看成+1,)看成-1,问题就变成了求左起连续最小一段和右起连续最大一段。
由于还涉及到修改,我们考虑用Splay动态维护,首先要维护基本的五个:father,left,right,size,s(这个元素的值)。涉及到翻转,所以我们维护四个答案需要的信息:xl,xr,yl,yr,sum,表示左起、右起最多的+1个数,左起、右起最多的-1个数,子树和,且这里xl,xr,yl,yr>=0,sum可正可负。
对于统一,记一个sa标志,表示该子树所有元素都变为这一个。
对于翻转,记一个rev标志,表示该子树是否翻转。
对于改变,记一个en标志,表示该子树是否改变。
并且三者都需要下传标记。下传时s,xl,xr,yl,yr,sum都需维护。统一比较简单,翻转要交换xl和xr,yl和yr,改变要交换xl和yl,xr和yr。而rev还需将左右子树对换(序列的顺序改变)。
每次对于提取区间(a,b),分情况讨论。
1、整个区间——区间对应为根
2、(1,x)——x+1旋到根,区间对应为根的左子树
3、(x,n)——x-1旋到根,区间对应为根的右子树
4、(x,y)——x-1旋到根,接着y+1旋到根的右子树,区间对应为根的右子树的左子树
每次操作提取出来区间对应的子树节点后进行操作,并将到根的路径上的节点再维护一下。
问题到这似乎已经完美解决了。
但是,我们要注意到一个极其棘手的问题,如果一个节点同时存在统一,翻转,改变,先传哪一个?首先,翻转是没有问题的,先翻转再统一,或先统一再翻转都一样,先翻转再改变,或先改变再翻转也都一样,我们不妨把它的顺序放在第一位。然而先统一再改变与先改变再统一却会出问题的,两者会有不一样的结果,到底哪个在前?只能视标记的最初时间而定。统一的时间早则先统一,否则先改变。于是我们马上想到记录一个标记时间,下传时,将标记时间一同下传即可。但是这样做仍是会出问题的。为什么?
我们对一个节点(开始什么标记也没有)进行三次操作(假设中间不下传):改变,统一,改变。那么我们会发现,原本我们记录的boolean类型的en两次操作后变成了false,而统一符号仍然在,我们下次要下传时,子树的标记就完全不对了。为什么?因为我们之前考虑的是两次改变等价于没改,但是如果中间有统一标志,那么其实就是不等价了。
解决这一问题的方法可以是,先把改变放在前,统一放在后。那么我们只需特判一种情况:先统一,再改变,而这种情况出现的条件是在先改变下传标记时,有统一标记且统一标记的时间比当前改变下传标记早,那么我们就只需将改变记为false,将统一变为其相反数,并维护即可。也即强制使统一标记的时间大于改变下传标记时间。至此问题解决的主要过程已清晰可见了。
空间复杂度O(N)。建树O(N),单次询问O(logN),总时间复杂度O(N+MlogN)。
const maxn=100000; var n,m,i,j,k,p,x,y,root,tmp:longint;c:char;str:string; rev,en:array[0..maxn] of boolean; l,r,fa,sum,s,size,sa,xl,xr,yl,yr,satime,entime:array[0..maxn] of longint; function reads:string; begin reads:=''; repeat read(c); if c=' ' then break; reads:=reads+c; until eoln; end; function readn:longint; begin readn:=0; repeat read(c); if c=' ' then break; readn:=readn*10+ord(c)-ord('0'); until eoln; end; function max(a,b:longint):longint; begin if a>b then exit(a) else exit(b); end; procedure prev(i:longint); begin rev[i]:=not rev[i]; tmp:=xl[i];xl[i]:=xr[i];xr[i]:=tmp; tmp:=yl[i];yl[i]:=yr[i];yr[i]:=tmp; tmp:=l[i];l[i]:=r[i];r[i]:=tmp; end; procedure psa(i,t,time:longint); begin satime[i]:=time; sa[i]:=t; s[i]:=t; sum[i]:=t*size[i]; if t=1 then begin xl[i]:=sum[i];xr[i]:=sum[i];yl[i]:=0;yr[i]:=0;end else begin xl[i]:=0;xr[i]:=0;yl[i]:=-sum[i];yr[i]:=-sum[i];end; end; procedure pen(i,time:longint); begin if (satime[i]>0)and(satime[i]<time) then begin en[i]:=false; psa(i,-sa[i],time); exit; end; entime[i]:=time; en[i]:=not en[i]; s[i]:=-s[i]; sum[i]:=-sum[i]; tmp:=xl[i];xl[i]:=yl[i];yl[i]:=tmp; tmp:=xr[i];xr[i]:=yr[i];yr[i]:=tmp; end; procedure pup(i:longint); begin size[i]:=size[l[i]]+size[r[i]]+1; sum[i]:=sum[l[i]]+sum[r[i]]+s[i]; xl[i]:=max(max(xl[l[i]],sum[l[i]]+s[i]+xl[r[i]]),0); xr[i]:=max(max(xr[r[i]],sum[r[i]]+s[i]+xr[l[i]]),0); yl[i]:=max(max(yl[l[i]],-sum[l[i]]-s[i]+yl[r[i]]),0); yr[i]:=max(max(yr[r[i]],-sum[r[i]]-s[i]+yr[l[i]]),0); end; procedure pdown(i:longint); begin if rev[i] then begin if l[i]>0 then prev(l[i]); if r[i]>0 then prev(r[i]); rev[i]:=false; end; if en[i] then begin pen(l[i],entime[i]); pen(r[i],entime[i]); en[i]:=false; entime[i]:=0; end; if sa[i]<>0 then begin if l[i]>0 then psa(l[i],sa[i],satime[i]); if r[i]>0 then psa(r[i],sa[i],satime[i]); sa[i]:=0; satime[i]:=0; end; end; procedure left(i:longint); var x,y:longint; begin x:=r[i]; y:=l[x]; r[i]:=y; if y>0 then fa[y]:=i; if fa[i]>0 then if l[fa[i]]=i then l[fa[i]]:=x else r[fa[i]]:=x; fa[x]:=fa[i]; l[x]:=i; fa[i]:=x; pup(i); if i=root then root:=x; end; procedure right(i:longint); var x,y:longint; begin x:=l[i]; y:=r[x]; l[i]:=y; if y>0 then fa[y]:=i; if fa[i]>0 then if l[fa[i]]=i then l[fa[i]]:=x else r[fa[i]]:=x; fa[x]:=fa[i]; r[x]:=i; fa[i]:=x; pup(i); if i=root then root:=x; end; procedure splay(x,f:longint); var y,z:longint; begin pdown(x); while fa[x]<>f do begin y:=fa[x]; z:=fa[y]; if z>0 then pdown(z); if y>0 then pdown(y); if z=f then if l[y]=x then right(y) else left(y) else if l[z]=y then if l[y]=x then begin right(z);right(y);end else begin left(y);right(z);end else if l[y]=x then begin right(y);left(z);end else begin left(z);left(y);end; end; pup(x); end; procedure sel(x,f:longint); var t:longint; begin t:=root; repeat pdown(t); if x=size[l[t]]+1 then break else if x<size[l[t]]+1 then t:=l[t] else begin dec(x,size[l[t]]+1);t:=r[t];end; until false; splay(t,f); end; function tiqu(x,y:longint):longint; begin if x=1 then if y=n then exit(root) else begin sel(y+1,0);exit(l[root]);end else if y=n then begin sel(x-1,0);exit(r[root]);end else begin sel(x-1,0);sel(y+1,root);exit(l[r[root]]);end; end; procedure build(x,y,f,t:longint); begin if f=0 then root:=(x+y) div 2 else if t=0 then l[f]:=(x+y) div 2 else r[f]:=(x+y) div 2; fa[(x+y) div 2]:=f; if x<=(x+y) div 2-1 then build(x,(x+y) div 2-1,(x+y) div 2,0); if (x+y) div 2+1<=y then build((x+y) div 2+1,y,(x+y) div 2,1); pup((x+y) div 2); end; procedure print(i:longint); begin pdown(i); if l[i]>0 then print(l[i]); if s[i]=1 then write('(') else write(')'); if r[i]>0 then print(r[i]); pup(i); end; function check:boolean; var i:longint; begin for i:=1 to n do if (xl[i]<0)or(xr[i]<0)or(yl[i]<0)or(yr[i]<0) then exit(false); exit(true); end; begin assign(input,'Brackets.in'); assign(output,'Brackets.out'); reset(input);rewrite(output); readln(n,m); for i:=1 to n do begin read(c); if c='(' then s[i]:=1 else s[i]:=-1; end; readln; build(1,n,0,0); for i:=1 to m do begin str:=reads; x:=readn; y:=readn; p:=tiqu(x,y); if str='Query' then begin writeln((yl[p]+1) div 2+(xr[p]+1) div 2); end else if str='Replace' then begin read(c); if c='(' then psa(p,1,i) else psa(p,-1,i); while fa[p]>0 do begin pup(fa[p]);p:=fa[p];end; end else if str='Swap' then begin prev(p); while fa[p]>0 do begin pup(fa[p]);p:=fa[p];end; end else if str='Invert' then begin pen(p,i); while fa[p]>0 do begin pup(fa[p]);p:=fa[p];end; end; readln; //print(root); //writeln; //if not check then writeln('no'); end; close(input);close(output); end.