bzoj 4650: [Noi2016]优秀的拆分
随便后缀数组跑一跑n^2就有95分,简直不能再划算啊。
正解如图。然后差分就好了。
灵魂画手。
这两天bug奇多,难得1A,老泪纵横。
//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<ctime>
const int N=100007;
typedef long long LL;
using namespace std;
int T,n,sa1[N],sa2[N],h1[N][20],h2[N][20],rak1[N],rak2[N],cl[N],cr[N],cc1[N],cc2[N];
char s[N];
template<typename T> void read(T &x) {
T f=1; x=0; char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
int cmp(int a,int b,int k,int y[]) {
int o1=a+k>=n?-1:y[a+k];
int o2=b+k>=n?-1:y[b+k];
return (o1==o2)&&(y[a]==y[b]);
}
void make_hight(int sa[],int h[][20],int rank[]) {
for(int i=0;i<n;i++) rank[sa[i]]=i;
for(int i=0,k=0;i<n;i++) {
if(!rank[i]) continue;
if(k) k--;
int j=sa[rank[i]-1];
while(s[i+k]==s[j+k]) k++;
h[rank[i]-1][0]=k;
}
for(int j=1;j<20;j++)
for(int i=0;i+(1<<j-1)<n;i++)
h[i][j]=min(h[i][j-1],h[i+(1<<j-1)][j-1]);
}
void make_sa(int sa[],int h[][20],int rank[]) {
static int t1[N],t2[N],c[N];
int *x=t1,*y=t2,i,k,m='z'+1;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[i]=s[i]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=0;i<n;i++) sa[--c[x[i]]]=i;
for(k=1;k<=n;k<<=1) {
int p=0;
for(i=n-k;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[y[i]]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
swap(x,y); p=1; x[sa[0]]=0;
for(i=1;i<n;i++)
x[sa[i]]=cmp(sa[i-1],sa[i],k,y)?p-1:p++;
if(p>=n) break;
m=p;
}
make_hight(sa,h,rank);
}
int get_lcp(int l,int r,int h[][20],int rank[]) {
l=rank[l]; r=rank[r]; if(l>r) swap(l,r);
int k=0;
while(l+(1<<k)<=r) k++; k--;
return min(h[l][k],h[r-(1<<k)][k]);
}
void clear() {
memset(cc1,0,sizeof(cc1));
memset(cc2,0,sizeof(cc2));
}
int main() {
read(T);
while(T--) {
scanf("%s",s);
n=strlen(s);
make_sa(sa1,h1,rak1);
for(int i=0;i<n/2;i++) swap(s[i],s[n-i-1]);
make_sa(sa2,h2,rak2);
for(int l=1;l<=n/2;l++)
for(int i=l;i<n;i+=l) {
int j=i+l;
int x=j>=n?0:min(l-1,get_lcp(i,j,h1,rak1));
int y=j-1>=n?0:min(l,get_lcp(n-i,n-j,h2,rak2));
if(x+y<l) continue;
cc1[i-y]+=1; cc1[i+x-l+1]-=1;
cc2[i-y+2*l-1]+=1; cc2[i+x-l+1+2*l-1]-=1;
}
int sum1=0,sum2=0;
for(int i=0;i<n;i++) {
sum1+=cc1[i]; sum2+=cc2[i];
cl[i]=sum1; cr[i]=sum2;
}
LL ans=0;
for(int i=0;i+1<n;i++)
ans+=(LL)cr[i]*cl[i+1];
printf("%lld\n",ans);
if(T) clear();
}
return 0;
}
/*
1
aabaabaabaa
*/