[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)。

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.
原文地址:https://www.cnblogs.com/oldmanren/p/2262178.html