传送门
题解:
前前后后花了几个月的时间总算是把shadowice的比赛写到只剩一道题了,那道题是个很水的莫队不想写了。
然而这道题标算给了个很扯的后缀自动机上边分树合并。。。TM什么毒瘤玩意
考虑选择两个极长重复子串来计算答案,其实就是两个上一位不同的后缀的 l c p lcp lcp。
假设这个
l
c
p
lcp
lcp长度为
k
k
k,则它对答案的贡献为:
∑
i
=
max
(
k
+
1
−
k
2
,
1
)
min
(
k
,
k
1
)
i
(
k
−
i
+
1
)
\sum_{i=\max(k+1-k_2,1)}^{\min(k,k_1)}i(k-i+1)
i=max(k+1−k2,1)∑min(k,k1)i(k−i+1)
上下差分拆开发现是个 3 3 3次方和二次方前缀和。
然后按照 h e i g h t height height数组合并一下就行了,注意需要维护一下后缀的上一位,保证算的是极长串。
代码:
#include<bits/stdc++.h>
#define ll unsigned long long
#define re register
#define cs const
using std::cerr;
using std::cout;
cs int N=1e5+5;
int n;
char s[N];
int sa[N],rk[N],ht[N];
inline void radix_sort(int *x,int *y,int m,int n){
static int bin[N];
memset(bin+1,0,sizeof(int)*m);
for(int re i=1;i<=n;++i)++bin[x[i]];
for(int re i=1;i<=m;++i)bin[i]+=bin[i-1];
for(int re i=n;i;--i)sa[bin[x[y[i]]]--]=y[i];
}
inline void init(){
int *x=rk,*y=ht;
for(int re i=1;i<=n;++i)x[i]=s[i],y[i]=i;
radix_sort(x,y,128,n);
int m=128;
for(int re i=1,cnt=0;cnt<n;i<<=1){
cnt=0;
for(int re j=n-i+1;j<=n;++j)y[++cnt]=j;
for(int re j=1;j<=n;++j)if(sa[j]>i)y[++cnt]=sa[j]-i;
radix_sort(x,y,m,n);std::swap(x,y);
x[sa[1]]=1;cnt=1;
for(int re j=2;j<=n;++j)
x[sa[j]]=(y[sa[j]]==y[sa[j-1]]&&y[sa[j]+i]==y[sa[j-1]+i])?cnt:++cnt;
m=cnt;
}
for(int re i=1;i<=n;++i)rk[sa[i]]=i;
for(int re i=1,k=0,j;i<=n;ht[rk[i++]]=k)
for(k?--k:0,j=sa[rk[i]-1];s[i+k]==s[j+k];++k);
}
int k1,k2;
inline ll sq(ll x){return x*(x+1)/2;}
inline ll sq2(ll x){return x*(x+1)*(x<<1|1)/6;}
inline ll calc(int x){
if(x>=k1+k2)return 0;
ll s1=sq(std::min(x,k1))*(x+1)-sq2(std::min(x,k1));
ll s2=sq(std::max(k2,x)-k2)*(x+1)-sq2(std::max(k2,x)-k2);
return s1-s2;
}
ll f[N];
int fa[N],id[N];
int siz[N],cnt[N][26];
inline int getfa(int x){return x==fa[x]?x:fa[x]=getfa(fa[x]);}
inline ll get(int x,int y){
ll res=(ll)siz[x]*siz[y];
for(int re i=0;i<26;++i)res-=(ll)cnt[x][i]*cnt[y][i];
return res;
}
inline void merge(int x,int y){
siz[x]+=siz[y];
fa[y]=x;
for(int re i=0;i<26;++i)cnt[x][i]+=cnt[y][i];
}
ll ans;
signed main(){
#ifdef zxyoi
freopen("check.in","r",stdin);
#endif
scanf("%s",s+1);n=strlen(s+1);
std::cin>>k1>>k2;k1=std::min(k1,n);k2=std::min(k2,n);
init();
for(int re i=1;i<=n;++i)f[i]=calc(i);
for(int re i=1;i<=n;++i){
fa[i]=i;siz[i]=1;
if(sa[i]!=1)cnt[i][s[sa[i]-1]-'a']++;
}
for(int re i=2;i<=n;++i)id[i-1]=i;
std::sort(id+1,id+n,[](int x,int y){return ht[x]>ht[y];});
for(int re i=1;i<n;++i){
int x=getfa(id[i]),y=getfa(id[i]-1),len=ht[id[i]];
(f[len])&&(ans+=(ll)f[len]*get(x,y));
merge(x,y);
}
cout<<ans<<"\n";
return 0;
}