题解 CF653F 【Paper task】

Durant_Lee

2019-02-28 08:34:36

Solution

[欢迎来我的blog逛逛~](https://blog.csdn.net/Dream_Lolita/article/details/88010611) 【题目】 给定一个括号串,问有多少种不同的合法括号子串。$n\leq 5\times 10^5$ 【解题思路】 首先我们将左括号看作$1$,右括号看作$-1$,肯定要处理出一个前缀和数组。 如果不考虑重复,那么我们不妨考虑求以每个位置开头的合法子串个数,即对于每个位置$i$,我们需要求有多少个 $$i<j\leq n,\text{s.t.} s_j=s_{i-1},\forall i\leq k \leq j ,s_k\geq s_{i-1}$$ 这个问题我们可以先处理出每种前缀和的所有位置,并通过$\text{RMQ}$维护区间最小值,然后在之前处理出的位置数组中二分判断最大可选的$j$在哪个位置即可。 现在要求去重,我们可以求出$height$数组,然后$j$的可选范围就是 $$sa_i+height_i+1\leq j\leq n$$ 除此之外的做法都是一样的。 复杂度$O(n\log n)$ 【参考代码】 ```cpp #include<bits/stdc++.h> #define pb push_back using namespace std; typedef long long ll; const int N=1e6+10,M=500001; namespace SA { int sa[N],rk[N],hi[N]; int wa[N],wb[N],wx[N],wy[N]; bool cmp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];} void getsa(int *r,int n,int m) { int *x=wa,*y=wb,*t,i,j,p; for(i=0;i<m;++i) wx[i]=0; for(i=0;i<n;++i) wx[x[i]=r[i]]++; for(i=1;i<m;++i) wx[i]+=wx[i-1]; for(i=n-1;~i;--i) sa[--wx[x[i]]]=i; for(j=1,p=0;p<n;j<<=1,m=p) { for(p=0,i=n-j;i<n;++i) y[p++]=i; for(i=0;i<n;++i) if(sa[i]>=j) y[p++]=sa[i]-j; for(i=0;i<m;++i) wx[i]=0; for(i=0;i<n;++i) wx[wy[i]=x[y[i]]]++; for(i=1;i<m;++i) wx[i]+=wx[i-1]; for(i=n-1;~i;--i) sa[--wx[wy[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;++i) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++; } } void getheight(int *r,int n) { int i,j,k=0; for(i=1;i<=n;++i) rk[sa[i]]=i; for(i=0;i<n;hi[rk[i++]]=k) for(k?--k:0,j=sa[rk[i]-1];r[i+k]==r[j+k];++k); } void adjust(int n) { for(int i=n;i;--i) ++sa[i],rk[i]=rk[i-1]; sa[0]=rk[0]=0; } void output(int n) { for(int i=0;i<=n;++i) printf("%d ",sa[i]); puts(""); for(int i=0;i<=n;++i) printf("%d ",rk[i]); puts(""); for(int i=0;i<=n;++i) printf("%d ",hi[i]); puts(""); } void buildsa(int *r,int n,int m) { getsa(r,n+1,m);getheight(r,n);adjust(n); } } using namespace SA; namespace DreamLolita { ll ans; int n,s[N],Log[N],fc[25],h[20][N],sum[N]; char ch[N]; vector<int>vec[N]; void initrmq(int n) { for(int i=n;i;--i) s[i]=s[i-1],sum[i]=sum[i-1]; s[0]=0;sum[0]=M; for(int i=1;i<=n;++i) vec[sum[i]].pb(i); fc[0]=1;for(int i=1;i<22;++i)fc[i]=fc[i-1]<<1; for(int i=2;i<=n;++i) Log[i]=Log[i>>1]+1; for(int i=1;i<=n;++i) h[0][i]=sum[i]; for(int j=1;j<20;++j) for(int i=1;i+fc[j]-1<=n;++i) h[j][i]=min(h[j-1][i],h[j-1][i+fc[j-1]]); } int query(int l,int r) { if(l>r) swap(l,r); int t=Log[r-l+1]; return min(h[t][l],h[t][r-fc[t]+1]); } void solution() { scanf("%d%s",&n,ch); for(int i=0;i<n;++i) ch[i]=='('?(s[i]=sum[i]=1):(s[i]=2,sum[i]=-1); for(int i=1;i<n;++i) sum[i]+=sum[i-1]; for(int i=0;i<n;++i) sum[i]+=M; buildsa(s,n,N);initrmq(n); for(int i=1;i<=n;++i) { int x=sa[i]; if(s[x]==2) continue; int l=x+hi[i],r=n,res=l-1; while(l<=r) { int mid=(l+r)>>1; if(query(x,mid)>=sum[x-1]) l=mid+1,res=mid; else r=mid-1; } int t=sum[x-1]; ans+=upper_bound(vec[t].begin(),vec[t].end(),res)-lower_bound(vec[t].begin(),vec[t].end(),x+hi[i]); }//wrong because x+hi[i]->res printf("%lld\n",ans); } } int main() { #ifndef ONLINE_JUDGE freopen("CF653F.in","r",stdin); freopen("CF653F.out","w",stdout); #endif DreamLolita::solution(); return 0; } ```