[HNOI2011]括号修复

【出处】

HNOI2011Day1

【题目描述】

给定一个长度为N的由“(”和“)”组成的字符串和M个操作。

要求实现四个操作:

1、统一:将[a,b]之间的所有括号改成c(“(”或“)”)

2、翻转:将[a,b]之间的字符串翻转

3、改变:将[a,b]之间的‘(’变成‘)’

4、询问:询问[a,b]之间的字符串至少要改变多少位才能变成合法的括号序列。

N,M≤100000

【算法分析】

此题非常巧妙,可以成功转化成类似最大连续子序列的求解方式,并运用Splay进行维护。

对于一个已知的括号,我们该如何求出询问的答案?

比如)((())((这样一个,我们把符合的一层层去掉,就变成了)(((,那么很显然答案是3。并且易得,所有的字符串化简后必是))...)((...(的形式(或全是)或(),答案就是wps_clip_image-21258。关键就是如何求出个数来。考虑左起的),我们发现,当一个字符串)))))())中有了一个(,就相当于连续的(减少了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.
posted @   FancyCoder0  阅读(1426)  评论(0编辑  收藏  举报
编辑推荐:
· 基于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)
点击右上角即可分享
微信分享提示