洛谷 P3215 - 括号修复

DS真tm要人命

洛谷题目页面传送门

给定一个括号串\(a,|a|=n\)。你需要支持以下\(4\)\(q\)次操作:

  1. \(\texttt{Replace}\ l\ r\ x\):将\(a_{l\sim r}\)内的字符全部改为\(x\)
  2. \(\texttt{Swap}\ l\ r\):将\(a_{l\sim r}\)翻转;
  3. \(\texttt{Invert}\ l\ r\):将\(a_{l\sim r}\)内的所有字符\(\texttt(\to\texttt),\texttt)\to\texttt(\)
  4. \(\texttt{Query}\ l\ r\):查询\(a_{l\sim r}\)至少要改变多少个字符才能变成合法括号串。保证一定能在有限次改动内变成合法括号串。

\(n,q\in\left[1,10^5\right]\)

先来考虑怎么算一个括号串\(s\)至少要改变多少个字符才能变成合法括号串。由于这是一个复杂的数据结构题,所以我们坚信结论比较简单

显然,\(s\)能在有限次改动内变成合法括号串当且仅当\(2\mid |s|\)。以下默认\(2\mid |s|\)

\(\texttt(\)看成\(1\)\(\texttt)\)看成\(-1\)是解决括号串合法问题的惯用套路。设\(bal(x)=\begin{cases}1&x=\texttt(\\-1&x=\texttt)\end{cases}\)\(Bal_{s,i}=\sum\limits_{j=1}^ibal(s_j)\),则括号串\(s\)合法当且仅当\(\forall i\in[1,|s|],Bal_{s,i}\geq0\)\(Bal_{s,|s|}=0\)

考虑算出\(Bal_s\)数组。先假设只需要\(\forall i\in[1,|s|],Bal_{s,i}\geq0\)实现。对于每个\(i\)使得\(Bal_{s,i}<0\),我们要将\(s_i\)左边若干个\(\texttt)\)改成\(\texttt(\)使得\(Bal_{s,i}\geq0\)。显然,改一次会令\(Bal_{s,i}=Bal_{s,i}+2\),那么最少需要将\(s_i\)左边\(\left\lceil\dfrac{-Bal_{s,i}}2\right\rceil\)\(\texttt)\)改成\(\texttt(\)。又因为如果\(\left\lceil\dfrac{-Bal_{s,i}}2\right\rceil<\max\limits_{j=1}^{i-1}\left\{\left\lceil\dfrac{-Bal_{s,j}}2\right\rceil\right\}\)的话,那么前面必有至少\(\left\lceil\dfrac{-Bal_{s,i}}2\right\rceil\)次改动,已满足要求;否则显然可以找到办法只改前面的\(\left\lceil\dfrac{-Bal_{s,i}}2\right\rceil\)个括号使得前面的所有\(Bal\)值都非负。所以若只需要\(\forall i\in[1,|s|],Bal_{s,i}\geq0\),那么最少改动数为\(\max\limits_{i=1}^{|s|}\left\{\left\lceil\dfrac{-Bal_{s,i}}2\right\rceil\right\}\)

此时已经\(\forall i\in[1,|s|],Bal_{s,i}\geq0\)了。再加上\(Bal_{s,|s|}=0\)的条件。显然此时\(Bal_{s,|s|}\geq0\),我们要考虑将若干\(\texttt(\)改成\(\texttt)\)使得\(Bal_{s,|s|}=0\)。显然,改一次会令\(Bal_{s,|s|}=Bal_{s,|s|}-2\),那么最少需要将\(\dfrac{Bal_{s,|s|}}2\)\(\texttt(\)改成\(\texttt)\)。又因为先前\(\texttt)\to\texttt(\)的那些改动是保障\(\forall i\in[1,|s|],Bal_{s,i}\geq0\)的基础,肯定动不得,所以只能另外挑选\(\dfrac{Bal_{s,|s|}}2\)\(\texttt(\)改成\(\texttt)\)。于是得出结论:括号串\(s\)至少要改变\(\max\limits_{i=1}^{|s|}\left\{\left\lceil\dfrac{-Bal_{s,i}}2\right\rceil\right\}+\dfrac{Bal_{s,|s|}+2\max\limits_{i=1}^{|s|}\left\{\left\lceil\frac{-Bal_{s,i}}2\right\rceil\right\}}2=\left\lceil\dfrac{-\min\limits_{i=1}^{|s|}\{Bal_{s,i}\}}2\right\rceil+\dfrac{Bal_{s,|s|}+2\left\lceil\frac{-\min\limits_{i=1}^{|s|}\{Bal_{s,i}\}}2\right\rceil}2\)个字符才能变成合法括号串。

所以我们只需要维护\(\min\limits_{i=1}^{r-l+1}\{Bal_{a_{l\sim r},i}\}\)\(Bal_{a_{l\sim r},r-l+1}\)这两个值即可。考虑到有区间翻转操作,我们用平衡树维护,这里使用fhq-Treap。

设当前节点\(i\)表示\(a_x\),子树表示区间\([l,r]\),那么我们需要存储\(v_i=bal(a_x),bal\_all_i=Bal_{a_{l\sim r},r-l+1},Mn\_bal_i=\min\limits_{j=1}^{r-l+1}\{Bal_{a_{l\sim r},j}\}\)。为了在区间反转操作中\(\mathrm O(1)\)打懒标记,我们还需要存储\(mn\_baL_i\)。为了在区间取反操作中\(\mathrm O(1)\)打懒标记,我们还需要存储\(Mx\_bal_i,mx\_baL_i\)。此时上传时很容易达到\(\mathrm O(1)\)。此外还需要存储\(3\)种操作的懒标记。

由于有\(3\)种懒标记,我们需要强行规定下传时的顺序。不妨规定顺序为\(\texttt{Replace},\texttt{Swap},\texttt{Invert}\)。此时打懒标记时除了应该做的常规操作以外,打\(\texttt{Replace}\)的懒标记时要将另\(2\)个懒标记清空。这里还有一个注意的地方:下传懒标记时有一个原则,就是参数不能含有非懒标记的其他存储的值,因为它们只能表现此节点的现状,并不能表现它经历了什么。我之前直接将\(\texttt{Replace}\)的懒标记定义为bool,然后下传时的参数为\(v_i\),这样由于\(\texttt{Invert}\)的存在会出错,导致我盯着电脑看&自闭了一晚上。所以要定义为int当作下传时的参数,\(\texttt{Invert}\)时只改\(v_i\)不改\(\texttt{Replace}\)的懒标记。

这还是一个比较模板的平衡树题吧……

(这个毒瘤卡常题还需要开O3优化才能过……)

下面是AC代码:

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
#define X first
#define Y second
const int inf=0x3f3f3f3f;
mt19937 rng(20060617/*信仰优化*/);
const int N=100000;
int n/*括号串长度*/,qu/*操作数*/;
char a[N+5];//括号串 
struct fhq_treap{//fhq-Treap 
	int sz/*点数*/,root/*根*/;
	struct node{unsigned key;int lson,rson,sz,v,bal_all,Mn_bal,mn_baL,Mx_bal,mx_baL,lz_chg/*Replace懒标记*/;bool lz_rev/*Swap懒标记*/,lz_inv/*Invert懒标记*/;}nd[N+1];
	#define key(p) nd[p].key
	#define lson(p) nd[p].lson
	#define rson(p) nd[p].rson
	#define sz(p) nd[p].sz
	#define v(p) nd[p].v
	#define bal_all(p) nd[p].bal_all
	#define Mn_bal(p) nd[p].Mn_bal
	#define mn_baL(p) nd[p].mn_baL
	#define Mx_bal(p) nd[p].Mx_bal
	#define mx_baL(p) nd[p].mx_baL
	#define lz_chg(p) nd[p].lz_chg
	#define lz_rev(p) nd[p].lz_rev
	#define lz_inv(p) nd[p].lz_inv
	void sprup(int p){//上传 
		sz(p)=sz(lson(p))+1+sz(rson(p));
		bal_all(p)=bal_all(lson(p))+v(p)+bal_all(rson(p));
		Mn_bal(p)=min(Mn_bal(lson(p)),bal_all(lson(p))+v(p)+min(0,Mn_bal(rson(p))));
		mn_baL(p)=min(mn_baL(rson(p)),bal_all(rson(p))+v(p)+min(0,mn_baL(lson(p))));
		Mx_bal(p)=max(Mx_bal(lson(p)),bal_all(lson(p))+v(p)+max(0,Mx_bal(rson(p))));
		mx_baL(p)=max(mx_baL(rson(p)),bal_all(rson(p))+v(p)+max(0,mx_baL(lson(p))));
	}
	int nwnd(int v){return nd[++sz]=node({rng(),0,0,1,v,v,v,v,v,v,0,false,false}),sz;}//新建节点 
	int bld(int l=1,int r=n){//建树 
		int mid=l+r>>1,p=nwnd(a[mid]=='('?1:-1);
		if(l<=mid-1)lson(p)=bld(l,mid-1);
		if(mid+1<=r)rson(p)=bld(mid+1,r);
		return sprup(p),p;
	}
	void init(){//fhq-Treap初始化 
		nd[sz=0]=node({0,0,0,0,0,0,inf,inf,-inf,-inf,0,0,0});
		root=bld();
	}
	void sprdwn_chg(int p,int v){//打Replace懒标记 
		v(p)=lz_chg(p)=v;
		bal_all(p)=v*sz(p);
		if(~v)Mn_bal(p)=mn_baL(p)=1,Mx_bal(p)=mx_baL(p)=sz(p);
		else Mx_bal(p)=mx_baL(p)=-1,Mn_bal(p)=mn_baL(p)=-sz(p);
		lz_rev(p)=lz_inv(p)=false;
	}
	void sprdwn_rev(int p){//打Swap懒标记 
		swap(lson(p),rson(p));
		swap(Mn_bal(p),mn_baL(p));
		swap(Mx_bal(p),mx_baL(p));
		lz_rev(p)^=1;
	}
	void sprdwn_inv(int p){//打Invert懒标记 
		v(p)=-v(p);
		bal_all(p)=-bal_all(p);
		swap(Mn_bal(p),Mx_bal(p));Mn_bal(p)=-Mn_bal(p);Mx_bal(p)=-Mx_bal(p);
		swap(mn_baL(p),mx_baL(p));mn_baL(p)=-mn_baL(p);mx_baL(p)=-mx_baL(p);
		lz_inv(p)^=1;
	}
	void sprdwn(int p){//下传 
		if(lz_chg(p)){
			if(lson(p))sprdwn_chg(lson(p),lz_chg(p));
			if(rson(p))sprdwn_chg(rson(p),lz_chg(p));
			lz_chg(p)=0;
		}
		if(lz_rev(p)){
			if(lson(p))sprdwn_rev(lson(p));
			if(rson(p))sprdwn_rev(rson(p));
			lz_rev(p)=false;
		}
		if(lz_inv(p)){
			if(lson(p))sprdwn_inv(lson(p));
			if(rson(p))sprdwn_inv(rson(p));
			lz_inv(p)=false;
		}
	}
	pair<int,int> split(int x,int p=-1){~p||(p=root);
		if(!x)return mp(0,p);
		pair<int,int> sp;
		sprdwn(p);
		if(x<=sz(lson(p)))return sp=split(x,lson(p)),lson(p)=sp.Y,sprup(p),mp(sp.X,p);
		return sp=split(x-sz(lson(p))-1,rson(p)),rson(p)=sp.X,sprup(p),mp(p,sp.Y);
	}
	int mrg(int p,int q){
		if(!p||!q)return p|q;
		sprdwn(p);sprdwn(q);
		if(key(p)<key(q))return rson(p)=mrg(rson(p),q),sprup(p),p;
		return lson(q)=mrg(p,lson(q)),sprup(q),q;
	}
	void chg(int l,int r,char v){//区间赋值 
		pair<int,int> sp=split(l-1),sp0=split(r-l+1,sp.Y);
		sprdwn_chg(sp0.X,v=='('?1:-1);
		root=mrg(mrg(sp.X,sp0.X),sp0.Y);
	}
	void rev(int l,int r){//区间翻转 
		pair<int,int> sp=split(l-1),sp0=split(r-l+1,sp.Y);
		sprdwn_rev(sp0.X);
		root=mrg(mrg(sp.X,sp0.X),sp0.Y);
	}
	void inv(int l,int r){//区间取反 
		pair<int,int> sp=split(l-1),sp0=split(r-l+1,sp.Y);
		sprdwn_inv(sp0.X);
		root=mrg(mrg(sp.X,sp0.X),sp0.Y);
	}
	int least(int l,int r){//查询操作 
		pair<int,int> sp=split(l-1),sp0=split(r-l+1,sp.Y);
		int tmp1=max(0,(-Mn_bal(sp0.X)+1)/2),tmp2=bal_all(sp0.X);
//		cout<<Mn_bal(sp0.X)<<" "<<bal_all(sp0.X)<<"\n";
		return root=mrg(mrg(sp.X,sp0.X),sp0.Y),tmp1+(tmp2+2*tmp1)/2;
	}
	void dfs(int p=-1)/*调试用*/{~p||(p=root);
		if(!p)return;
		sprdwn(p);
		dfs(lson(p));
//		printf("node#%d:lson=%d rson=%d v=%d all=%d Mn=%d mn=%d Mx=%d mx=%d\n",p,lson(p),rson(p),v(p),bal_all(p),Mn_bal(p),mn_baL(p),Mx_bal(p),mx_baL(p));
//		putchar(~v(p)?'(':')');
		dfs(rson(p));
	}
}trp;
int main(){
	cin>>n>>qu>>a+1;
	trp.init();//fhq-Treap初始化 
	while(qu--){
		string tp;int x,y;char z;
		cin>>tp>>x>>y;
		if(tp=="Replace")cin>>z,trp.chg(x,y,z);
		else if(tp=="Swap")trp.rev(x,y);
		else if(tp=="Invert")trp.inv(x,y);
		else cout<<trp.least(x,y)<<"\n";
//		trp.dfs();//puts("");
	}
	return 0;
}
posted @ 2020-04-21 20:54  ycx060617  阅读(148)  评论(1编辑  收藏  举报