bzoj 3238 [Ahoi2013]差异 后缀数组+单调栈
题目大意
给定长度为n<=500000的字符串S
\(T_i\)表示i开始的后缀
求\(\sum_{1<=i<j<=n}len(T_i)+len(T_j)-2*lcp(T_i,T_j)\)
分析
后缀数组+单调栈
注意
long long
solution
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
typedef long long LL;
using namespace std;
const int M=1000007;
char s[M];
int n;
LL ans=0;
int sa[M],t[M];
int rk[M],f[M];
int sum[M],h[M];
void getsa(){
int i,j,p,nw;
memset(sum,0,sizeof(sum));
for(i=1;i<=n;i++) sum[s[i]]++;
for(i=1;i<=500;i++) sum[i]+=sum[i-1];
for(i=n;i>0;i--) sa[sum[s[i]]--]=i;
for(p=0,i=1;i<=n;i++) rk[sa[i]]=(s[sa[i]]!=s[sa[i-1]])?(++p):(p);
for(j=1,nw=p;nw!=n;j<<=1,nw=p){
memset(sum,0,sizeof(sum));
memcpy(f,rk,sizeof(rk));
for(p=0,i=n-j+1;i<=n;i++) t[++p]=i;
for(i=1;i<=n;i++) if(sa[i]>j) t[++p]=sa[i]-j;
for(i=1;i<=n;i++) sum[f[i]]++;
for(i=1;i<=nw;i++) sum[i]+=sum[i-1];
for(i=n;i>0;i--) sa[sum[f[t[i]]]--]=t[i];
for(p=0,i=1;i<=n;i++) rk[sa[i]]=(f[sa[i]]!=f[sa[i-1]]||f[sa[i]+j]!=f[sa[i-1]+j])?(++p):(p);
}
}
void geth(){
int i,j,p=0;//p=0
for(i=1;i<=n;i++){
j=sa[rk[i]-1];
for(;i+p<=n&&j+p<=n&&s[i+p]==s[j+p];p++);
h[rk[i]]=p;
if(p>0) p--;
}
h[1]=0;
}
struct node{
int h,num;
node(int hh=0,int nn=0){h=hh;num=nn;}
}st[M];
int tot=0;
LL solve(){
LL res=0;
LL cnt=0;
int num;
for(int i=1;i<=n;i++){
num=1;
cnt+=h[i];
for(;tot>0&&st[tot].h>=h[i];tot--){
num+=st[tot].num;
cnt-=(LL)(st[tot].h-h[i])*st[tot].num;
}
st[++tot]=node(h[i],num);
res+=cnt;
}
return res*2;
}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
int i;
for(i=1;i<=n;i++) ans+=(LL)(n-i+1)*(n-1);
getsa();
geth();
LL tp=solve();
printf("%lld\n",ans-tp);
return 0;
}