bzoj4650: [Noi2016]优秀的拆分

来源:互联网 发布:scratch儿童学编程 编辑:程序博客网 时间:2024/05/20 06:36

此题有95分暴力。。。

发现AABB的统计相当于对AA的统计。于是可以计算出每个点左侧为AA的方案数。枚举|A|,将字符串分成长度为|A|的小段,当AA中点在小段中的情况可以利用SA+rmq,O(1)求。于是就能在O(n/1+n/2+...+n/n)=O(nlogn)内求解。

#include<iostream>#include<cstdio>#include<cstring>#define N 80005using namespace std;int n,T,A[N],B[N],lg[N],bit[20];long long Ans;struct SuffixArray{char a[N];int SA[N],Rk[N],Ht[N],X[N],Y[N],v[N],Min[N][20];void GetSA(int n,int m=256){int *x=X,*y=Y,i,p,j;memset(v,0,sizeof v);for(i=1;i<=n;i++)v[x[i]=a[i]]++;for(i=1;i<=m;i++)v[i]+=v[i-1];for(i=n;i>=1;i--)SA[v[x[i]]--]=i;for(i=1;i<=n;i<<=1,m=p){p=0;memset(v,0,sizeof v);for(j=n-i+1;j<=n;j++)y[++p]=j;for(j=1;j<=n;j++)if(SA[j]>i)y[++p]=SA[j]-i;for(j=1;j<=n;j++)v[x[y[j]]]++;for(j=1;j<=m;j++)v[j]+=v[j-1];for(j=n;j>=1;j--)SA[v[x[y[j]]]--]=y[j];swap(x,y);p=1;x[SA[1]]=1;for(j=2;j<=n;j++)if(y[SA[j-1]]==y[SA[j]]&&y[SA[j-1]+i]==y[SA[j]+i]) x[SA[j]]=p;else x[SA[j]]=++p;if(p>=n)break;}}void GetHt(int n){int k=0;for(int i=1;i<=n;i++)Rk[SA[i]]=i;for(int i=1;i<=n;i++){if(k)k--;int j=SA[Rk[i]-1];while(i+k<=n&&j+k<=n&&a[i+k]==a[j+k])k++;Ht[Rk[i]]=k;}}void Get(int n){memset(SA,0,sizeof SA);memset(Ht,0,sizeof Ht);memset(Rk,0,sizeof Rk);memset(X,0,sizeof X);memset(Y,0,sizeof Y);memset(Min,0,sizeof Min);GetSA(n);GetHt(n);for (int i=1;i<=n;i++)Min[i][0]=Ht[i];for (int i=1;i<=15;i++)for (int j=1;j<=n;j++)Min[j][i]=min(Min[j][i-1],Min[j+bit[i-1]][i-1]);}int lcp(int x,int y){if (Rk[x]>Rk[y]) swap(x,y);x=Rk[x];y=Rk[y];int t=lg[y-x];return min(Min[x+1][t],Min[y-bit[t]+1][t]);}}S1,S2;int main(){bit[0]=1;for (int i=1;i<=15;i++) bit[i]=bit[i-1]<<1;for (int i=0,j=1,next=2;i<=15;i++,next<<=1)for (;j<=next;j++) lg[j]=i;scanf("%d",&T);while(T--){Ans=0;memset(A,0,sizeof A);memset(B,0,sizeof B);scanf("%s",S1.a+1);n=strlen(S1.a+1);for (int i=1;i<=n;i++) S2.a[n-i+1]=S1.a[i];S1.Get(n);S2.Get(n);for (int i=1;i<=n/2;i++){for (int j=1;j+i<=n;j+=i){int x=S1.lcp(j,j+i),y=S2.lcp(n-j+1,n-j-i+1);int l=max(j,j-y+i),r=min(j+i,j+x)-1;if (l<=r){A[l+i]++;A[r+i+1]--;B[l-i]++;B[r-i+1]--;}}}for (int i=1;i<=n;i++)A[i]+=A[i-1],B[i]+=B[i-1];for (int i=1;i<=n;i++)Ans+=A[i]*B[i];printf("%lld\n",Ans);}}


0 0