bzoj 4650: [Noi2016]优秀的拆分

来源:互联网 发布:单页用什么软件有哪些 编辑:程序博客网 时间:2024/05/17 18:25

       原来只会两个log平衡树合并。。后来围观claris在uoj群上秒题后会了这道题。

       枚举A(B)的长度L,然后枚举i=kL,考虑前缀i,i+L和后缀i+1,i+L+1,求出前缀的lcp和后缀的lcp,然后合法的方案就在一个方案内。差分一下即可。注意要避免重复。

AC代码如下:

#include<iostream>#include<cstdio>#include<cstring>#define N 30005using namespace std;int n,bin[25],lg2[N],q[N],sum[N],num[N]; char s[N];struct saffix{int a[N],sa[N],f[15][N],rnk[N<<1];void getsa(){int i,j,k,cnt;memset(sum,0,sizeof(int)*26); memset(rnk+n+1,0,sizeof(int)*n);for (i=1; i<=n; i++) sum[a[i]]++;for (i=1; i<26; i++) sum[i]+=sum[i-1];for (i=n; i; i--) sa[sum[a[i]]--]=i;for (i=1,cnt=0; i<=n; i++){if (i==1 || a[sa[i]]!=a[sa[i-1]]) cnt++;rnk[sa[i]]=cnt;}for (k=1; cnt<n; k<<=1){for (i=1; i<=k; i++) q[i]=n-k+i;for (i=1,j=k; i<=n; i++) if (sa[i]>k) q[++j]=sa[i]-k;memset(sum,0,sizeof(sum));for (i=1; i<=n; i++) sum[rnk[i]]++;for (i=1; i<=cnt; i++) sum[i]+=sum[i-1];for (i=n; i; i--) sa[sum[rnk[q[i]]]--]=q[i];for (i=1,cnt=0; i<=n; i++){if (i==1 || rnk[sa[i]]!=rnk[sa[i-1]] || rnk[sa[i]+k]!=rnk[sa[i-1]+k]) cnt++;q[sa[i]]=cnt;}for (i=1; i<=n; i++) rnk[i]=q[i];}}void getf(){int i,j,k;for (i=1,k=0; i<=n; i++){if (k) k--;j=sa[rnk[i]-1];while (i+k<=n && j+k<=n && a[i+k]==a[j+k]) k++;f[0][rnk[i]]=k;}}int lcp(int x,int y){if (y>n) return 0;x=rnk[x]; y=rnk[y]; if (x>y) swap(x,y);int k=lg2[y-x];return min(f[k][x+1],f[k][y-bin[k]+1]);}void pwk(){int i,j;for (i=1; i<=n; i++) a[i]=s[i]-'a';getsa(); getf();for (i=1; i<=14; i++)for (j=1; j<=n; j++){f[i][j]=f[i-1][j];if (j+bin[i-1]<=n) f[i][j]=min(f[i][j],f[i-1][j+bin[i-1]]);}}}ta,tb;int main(){int cas,i; scanf("%d",&cas);bin[0]=1;for (i=1; i<=14; i++){ bin[i]=bin[i-1]<<1; lg2[bin[i]]=i; }for (i=2; i<=30000; i++) if (!lg2[i]) lg2[i]=lg2[i-1];while (cas--){scanf("%s",s+1); n=strlen(s+1);int j,x,y;ta.pwk();for (i=1; i<=(n>>1); i++) swap(s[i],s[n-i+1]);tb.pwk();memset(sum,0,sizeof(sum)); memset(num,0,sizeof(num));for (i=1; i<(n>>1); i++)for (j=i; j+i<=n; j+=i){x=max(j-i+1,j-tb.lcp(n-j+1,n-j-i+1)+1);                   y=min(j,j-i+1+ta.lcp(j+1,j+i+1));if ((x==10 || y==10) && i==2){}if (x<=y){sum[x]++; sum[y+1]--;num[x+(i<<1)]++; num[y+(i<<1)+1]--;}}long long ans=0;for (i=2; i<=n; i++){sum[i]+=sum[i-1]; num[i]+=num[i-1];ans+=sum[i]*num[i];}printf("%lld\n",ans);}return 0;}


by lych

2016.8.4

0 0