[DS记录]CF1340F Nastya and CBS
command_block
2021-07-04 17:26:28
**题意** : 你需要动态维护一个多种括号组成的,长度为 $n$ 的括号序列。需要支持两种操作:
- 单点修改
- 查询 $[l,r]$ 区间是否是一个合法的括号序列。
$n,q\leq 10^5$ ,时限$\texttt{4s}$ ,空限$\texttt{250M}$。
------------
先考虑如何暴力回答单组询问。
用栈维护括号序列的匹配。若出现左括号,加入栈中。若出现右括号,查看栈顶是否为同类,若是,则弹栈,若不是(或栈为空),失配。
- ## 分块
对于每个块内,跑一次括号匹配。
此处允许栈为空的情况,此时要将右括号加入栈中。
若匹配时内部已经出现了失配,则对该块打上标记。
若没有失配,最终剩下的括号形如 $\texttt{)\})]\{(([}$ ,左边是一系列右括号,右边是一系列左括号。
回答询问时,将散块和整块的括号用栈再跑一次匹配。
先把散块按类似整块的方法整理。于是现在有若干段 “右右右左左左” 的括号序列等待合并。
这要求第 $i$ 组的左括号与第 $i+1$ 组的右括号完全匹配。使用 $\rm Hash$ 加速检验,复杂度可以做到 $O(q\sqrt{n})$。
- ## (兔队)线段树
对于每个线段树节点,维护内部匹配完之后剩下的括号,以及失配标记。
记左侧的右括号为 $ls$ ,右侧的左括号为 $rs$。为了方便维护,我们可以将 $ls$ 翻转。
![](https://cdn.luogu.com.cn/upload/image_hosting/hy3fqoie.png)
记要合并的两个点为 $L,R$。考虑 $L.rs,R.ls$ ,不妨设 $R.ls$ 的长度较小,则将 $L.rs$ 抵消一部分,然后 $L.rs$ 剩余的一部分配上 $R.rs$ 为新的 $U.rs$ ,原来的 $L.ls$ 即为 $U.ls$。
于是,我们在合并时需要查询 $L.rs$ 前 $|R.ls|$ 个位置的 $\rm Hash$ 值。
记 $gR(U,k)$ 表示 $u.rs$ 长度为 $k$ 的前缀的 $\rm Hash$ 值。
若 $k=0$ 或者 $k=|U.rs|$ 可以直接返回结果。
否则,观察 $U.rs$ 是怎样得来的。
- $|L.rs|\leq |R.ls|$
这时 $U.rs=R.rs$ ,直接递归右儿子。
- $|L.rs|>|R.ls|$
这时 $U.rs=L.rs-R.ls+R.rs$ 。
若 $k\leq |L.rs|-|R.ls|$ ,直接递归左儿子。
若 $k> |L.rs|-|R.ls|$ ,则查询 $gR(R,k-(|L.rs|-|R.ls|))$ ,然后左侧加上 $L.rs-R.ls$。
综上, $gR$ 的复杂度为 $O(\log n)$ ,每次操作的复杂度也就是 $O(\log^2n)$ 的。
询问时需要将拆分出的节点用栈记录,特殊写一个 $\rm gL2$。
```cpp
#include<algorithm>
#include<cstdio>
#define ll long long
#define MaxN 100500
using namespace std;
const int mod1=998244353,mod2=1000000007,Bas=114514;
int pw1[MaxN],pw2[MaxN];
struct Str{int m,s1,s2;};
Str operator + (const Str &L,const Str &R)
{return (Str){L.m+R.m,(L.s1+1ll*pw1[L.m]*R.s1)%mod1,(L.s2+1ll*pw2[L.m]*R.s2)%mod2};}
Str operator - (const Str &S,const Str &T)
{return (Str){S.m-T.m,(S.s1-1ll*T.s1*pw1[S.m-T.m])%mod1,(S.s2-1ll*T.s2*pw2[S.m-T.m])%mod2};}
bool operator == (const Str &A,const Str &B)
{return A.m==B.m&&(A.s1-B.s1)%mod1==0&&(A.s2-B.s2)%mod2==0;}
struct Node{Str l,r;bool fl;}a[MaxN<<2];
void build(int k,Node &A){
if (k>0){A.l=(Str){0,0,0};A.r=(Str){1,k,k};}
else {A.l=(Str){1,-k,-k};A.r=(Str){0,0,0};}
}
Str qryR(int l,int r,int u,int m)
{
if (m==0)return (Str){0,0,0};
if (m==a[u].r.m)return a[u].r;
int mid=(l+r)>>1,L=u<<1,R=u<<1|1;
if (a[L].r.m<=a[R].l.m)return qryR(mid+1,r,R,m);
if (m<=a[L].r.m-a[R].l.m)return qryR(l,mid,L,m);
return (a[L].r-a[R].l)+qryR(mid+1,r,R,m-(a[L].r.m-a[R].l.m));
}
Str qryL(int l,int r,int u,int m)
{
if (m==0)return (Str){0,0,0};
if (m==a[u].l.m)return a[u].l;
int mid=(l+r)>>1,L=u<<1,R=u<<1|1;
if (a[R].l.m<=a[L].r.m)return qryL(l,mid,L,m);
if (m<=a[R].l.m-a[L].r.m)return qryL(mid+1,r,R,m);
return (a[R].l-a[L].r)+qryL(l,mid,L,m-(a[R].l.m-a[L].r.m));
}
void up(int tl,int tr,int u)
{
int l=u<<1,r=u<<1|1,mid=(tl+tr)>>1;
if (a[l].fl||a[r].fl){a[u].fl=1;return ;}
if (a[l].r.m>=a[r].l.m){
if (qryR(tl,mid,l,a[l].r.m-a[r].l.m)==a[l].r-a[r].l){
a[u].fl=0;
a[u].l=a[l].l;
a[u].r=(a[l].r-a[r].l)+a[r].r;
}else a[u].fl=1;
}else {
if (qryL(mid+1,tr,r,a[r].l.m-a[l].r.m)==a[r].l-a[l].r){
a[u].fl=0;
a[u].r=a[r].r;
a[u].l=(a[r].l-a[l].r)+a[l].l;
}else a[u].fl=1;
}
}
int s[MaxN];
void build(int l,int r,int u)
{
if (l==r){build(s[l],a[u]);return ;}
int mid=(l+r)>>1;
build(l,mid,u<<1);
build(mid+1,r,u<<1|1);
up(l,r,u);
}
int to,wfc;
void chg(int l,int r,int u)
{
if (l==r){build(wfc,a[u]);return ;}
int mid=(l+r)>>1;
if (to<=mid)chg(l,mid,u<<1);
else chg(mid+1,r,u<<1|1);
up(l,r,u);
}
struct Data{int l,r,u;}st[55];
Str sr[55];
int wfl,wfr,tn;bool fl;
Str qryR2(int p,int m)
{
if (m==0)return (Str){0,0,0};
if (m==sr[p].m)return sr[p];
Data &U=st[p];
if (m<=sr[p-1].m-a[U.u].l.m)return qryR2(p-1,m);
return (sr[p-1]-a[U.u].l)+qryR(U.l,U.r,U.u,m-(sr[p-1].m-a[U.u].l.m));
}
void qry(int l,int r,int u)
{
if (fl)return ;
if (wfl<=l&&r<=wfr){
if (a[u].fl||sr[tn].m<a[u].l.m){fl=1;return ;}
if (qryR2(tn,sr[tn].m-a[u].l.m)==sr[tn]-a[u].l){
st[++tn]=(Data){l,r,u};
sr[tn]=(sr[tn-1]-a[u].l)+a[u].r;
}else fl=1;
return ;
}int mid=(l+r)>>1;
if (wfl<=mid)qry(l,mid,u<<1);
if (mid<wfr)qry(mid+1,r,u<<1|1);
}
int n,m;
int main()
{
scanf("%d%*d",&n);
pw1[0]=pw2[0]=1;
for (int i=1;i<=n;i++){
pw1[i]=1ll*pw1[i-1]*Bas%mod1;
pw2[i]=1ll*pw2[i-1]*Bas%mod2;
}
for (int i=1;i<=n;i++)scanf("%d",&s[i]);
build(1,n,1);
scanf("%d",&m);
for (int i=1,op;i<=m;i++){
scanf("%d",&op);
if (op==1){
scanf("%d%d",&to,&wfc);
chg(1,n,1);
}else {
scanf("%d%d",&wfl,&wfr);
fl=tn=0;qry(1,n,1);
puts(fl||sr[tn].m ? "No" : "Yes");
}
}return 0;
}
```