两个后缀i,j的lcp,其实就是:min(height[k]) ,i+1<=k<=j。
所以这一部分的总和就是:height数组[2,n]内,每一区间的最小值之和。
考虑用二分+ST表来求和。
暴露了菜鸡不会写单调栈的事实…
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N= 5e5 + 5 ;
int n, m, ans;
int sum[ N] , rk[ N] , rk2[ N] , tp[ N] , sa[ N] , height[ N] ;
char s[ N] ;
int LOG[ N] , f[ N] [ 21 ] , g[ N] [ 21 ] ;
inline void qsort ( )
{
for ( register int i= 0 ; i<= m; ++ i) sum[ i] = 0 ;
for ( register int i= 1 ; i<= n; ++ i) sum[ rk[ i] ] ++ ;
for ( register int i= 1 ; i<= m; ++ i) sum[ i] + = sum[ i- 1 ] ;
for ( register int i= n; i>= 1 ; -- i) sa[ sum[ rk[ tp[ i] ] ] ] = tp[ i] , sum[ rk[ tp[ i] ] ] -- ;
}
inline void SA ( )
{
m= 130 ;
for ( register int i= 1 ; i<= n; ++ i) rk[ i] = s[ i] - '0' + 1 , tp[ i] = i;
qsort ( ) ;
int p= 0 ;
for ( register int len= 1 ; p< n; m= p, len<<= 1 )
{
p= 0 ;
for ( register int i= n- len+ 1 ; i<= n; ++ i) tp[ ++ p] = i;
for ( register int i= 1 ; i<= n; ++ i) if ( sa[ i] > len) tp[ ++ p] = sa[ i] - len;
qsort ( ) ;
memcpy ( rk2, rk, sizeof ( rk2) ) ;
p= 1 ; rk[ sa[ 1 ] ] = p;
for ( register int i= 2 ; i<= n; ++ i)
{
if ( rk2[ sa[ i] ] == rk2[ sa[ i- 1 ] ] && rk2[ sa[ i] + len] == rk2[ sa[ i- 1 ] + len] ) p= p; else p++ ;
rk[ sa[ i] ] = p;
}
}
}
inline void LCP ( )
{
for ( register int i= 1 ; i<= n; ++ i) rk[ sa[ i] ] = i;
int k= 0 ;
for ( register int i= 1 ; i<= n; ++ i)
{
if ( rk[ i] == 1 ) continue ;
if ( k) k-- ;
int j= sa[ rk[ i] - 1 ] ;
while ( i+ k<= n && j+ k<= n && s[ i+ k] == s[ j+ k] ) k++ ;
height[ rk[ i] ] = k;
}
}
signed main ( ) {
scanf ( "%s" , s+ 1 ) ;
n= strlen ( s+ 1 ) ;
SA ( ) ;
LCP ( ) ;
ans= ( n- 1 ) * n* ( n+ 1 ) / 2 ;
LOG[ 0 ] = - 1 ;
n-- ;
for ( register int i= 1 ; i<= n; ++ i) f[ i] [ 0 ] = height[ i+ 1 ] , LOG[ i] = LOG[ i>> 1 ] + 1 ;
for ( register int j= 1 ; j<= 19 ; ++ j)
for ( register int i= 1 ; i+ ( 1 << j) - 1 <= n; ++ i) f[ i] [ j] = min ( f[ i] [ j- 1 ] , f[ i+ ( 1 << ( j- 1 ) ) ] [ j- 1 ] ) ;
for ( register int i= 1 ; i<= n; ++ i) g[ i] [ 0 ] = height[ i+ 1 ] ;
for ( register int j= 1 ; j<= 19 ; ++ j)
for ( register int i= n; i- ( 1 << j) + 1 >= 1 ; -- i) g[ i] [ j] = min ( g[ i] [ j- 1 ] , g[ i- ( 1 << ( j- 1 ) ) ] [ j- 1 ] ) ;
for ( register int i= 1 ; i<= n; ++ i)
{
if ( ! height[ i+ 1 ] ) continue ;
int l, r, mid, ll, rr, s;
l= 1 ; r= i- 1 ; ll= i;
while ( l<= r)
{
mid= l+ r>> 1 ;
s= LOG[ ( i- 1 ) - mid+ 1 ] ;
if ( min ( g[ i- 1 ] [ s] , g[ mid+ ( 1 << s) - 1 ] [ s] ) > height[ i+ 1 ] ) ll= mid, r= mid- 1 ;
else l= mid+ 1 ;
}
l= i; r= n; rr= i;
while ( l<= r)
{
mid= l+ r>> 1 ;
s= LOG[ mid- i+ 1 ] ;
if ( min ( f[ i] [ s] , f[ mid- ( 1 << s) + 1 ] [ s] ) == height[ i+ 1 ] ) rr= mid, l= mid+ 1 ;
else r= mid- 1 ;
}
int len= rr- ll+ 1 ;
ans- = 2 * height[ i+ 1 ] * ( rr- i+ 1 ) * ( i- ll+ 1 ) ;
}
printf ( "%lld\n" , ans) ;
return 0 ;
}