zoukankan      html  css  js  c++  java
  • 洛谷 P3215

    DS真tm要人命

    洛谷题目页面传送门

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

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

    (n,qinleft[1,10^5 ight])

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

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

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

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

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

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

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

    由于有(3)种懒标记,我们需要强行规定下传时的顺序。不妨规定顺序为( exttt{Replace}, exttt{Swap}, exttt{Invert})。此时打懒标记时除了应该做的常规操作以外,打( exttt{Replace})的懒标记时要将另(2)个懒标记清空。这里还有一个注意的地方:下传懒标记时有一个原则,就是参数不能含有非懒标记的其他存储的值,因为它们只能表现此节点的现状,并不能表现它经历了什么。我之前直接将( exttt{Replace})的懒标记定义为bool,然后下传时的参数为(v_i),这样由于( exttt{Invert})的存在会出错,导致我盯着电脑看&自闭了一晚上。所以要定义为int当作下传时的参数,( exttt{Invert})时只改(v_i)不改( exttt{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)<<"
    ";
    		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
    ",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)<<"
    ";
    //		trp.dfs();//puts("");
    	}
    	return 0;
    }
    
  • 相关阅读:
    7段数码管绘制
    画五角星
    绘制正方形
    蟒蛇的绘制
    玫瑰花
    小猪佩奇
    数列求和
    水仙花数
    鸡兔同笼
    画国际象棋盘
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/Luogu-P3215.html
Copyright © 2011-2022 走看看