[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)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | 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 . |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)