DS真tm要人命
洛谷题目页面传送门
给定一个括号串(a,|a|=n)。你需要支持以下(4)种(q)次操作:
- ( exttt{Replace} l r x):将(a_{lsim r})内的字符全部改为(x);
- ( exttt{Swap} l r):将(a_{lsim r})翻转;
- ( exttt{Invert} l r):将(a_{lsim r})内的所有字符( exttt( o exttt), exttt) o exttt();
- ( 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;
}