CF653F Paper task 题解
统计一个后缀和数组 \(suf_i\),其中 )
为 \(1\),(
为 \(-1\),那么一个子串 \(s_{l,r}\) 是一个合法括号序列,当且仅当 \(\min_{l\leq k \leq r}suf_k\geq suf_{r+1}\),并且 \(suf_l=suf_{r+1}\)。
我们对这个串建立 SAM 之后,每个点表示的是某个前缀的一段区间的后缀。限制出来之后,可以使用 ST 表配合 vector
来找出满足上述限制的位置统计答案。总复杂度 \(O(n\log n)\)。
点击查看代码
const int N=5e5+13;
namespace ST{
const int logN=23;
#define log2 _log2
int log2[N],f[N][logN];
inline void _init(int n){for(int i=2;i<=n;++i)log2[i]=log2[i>>1]+1;}
inline void init(int n){
_init(n);
int k=log2[n]+1;
for(int j=1;j<=k;++j)
for(int i=1;i+(1<<j)-1<=n;++i) f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
inline int query(int l,int r){int k=log2[r-l+1];return min(f[l][k],f[r-(1<<k)+1][k]);}
#undef log2
}
std::vector<int> a[N<<1];
#define P(i) a[(i)+N]
int n,suf[N];
char s[N];
int nxt[N<<1],len[N<<1],ed[N<<1],ptot=1,lastpos=1,son[N<<1][2],zrzak[2];
inline int newpos(int nson[2],int nlen){return len[++ptot]=nlen,son[ptot][0]=nson[0],son[ptot][1]=nson[1],ptot;}
inline void insert(int c,int pos){
int p=lastpos,u=newpos(zrzak,len[p]+1);ed[u]=pos;
while(p&&!son[p][c]) son[p][c]=u,p=nxt[p];
lastpos=u;
if(!p) return nxt[u]=1,void();
int d=son[p][c];
if(len[d]==len[p]+1) nxt[u]=d;
else{
int v=newpos(son[d],len[p]+1);ed[v]=ed[d];
nxt[v]=nxt[d],nxt[d]=nxt[u]=v;
while(p&&son[p][c]==d) son[p][c]=v,p=nxt[p];
}
}
inline bool check(int k,int pos){return ST::query(k,pos-1)>=suf[pos];}
int main(){
read(n);read(s+1);
for(int i=1;i<=n;++i) insert(s[i]=='(',i);
for(int i=n;i;--i) suf[i]=suf[i+1]+(s[i]=='('?-1:1),ST::f[i][0]=suf[i];
for(int i=1;i<=n;++i) P(suf[i]).pb(i);
ST::init(n);ll ans=0;
for(int i=2;i<=ptot;++i){
int pos=ed[i]+1;int L=ed[i]-len[i]+1,R=ed[i]-len[nxt[i]];
int l=std::lower_bound(P(suf[pos]).begin(),P(suf[pos]).end(),L)-P(suf[pos]).begin(),r=std::upper_bound(P(suf[pos]).begin(),P(suf[pos]).end(),R)-P(suf[pos]).begin()-1;
int tmp=r;
if(l>r||!check(P(suf[pos])[r],pos)) continue;
while(l<r){
int mid=(l+r)>>1;
if(check(P(suf[pos])[mid],pos)) r=mid;
else l=mid+1;
}
ans+=tmp-l+1;
}
println(ans);
return 0;
}