題目傳送門
題意:
給定一個長度爲 的字符串 $S$,令 表示它從第 個字符開始的後綴。
計算 。
數據範圍: 。
題解:
首先我們掏出後綴數組板子,獲得 數組。
我們所需要求的其實是 ,其他的都是常數。
我們需要求的就是 數組的所有區間最小值的和。如何不明白,就仔細想一想。
我因爲不會求所有區間最小值的和,被辦了一下午和一晚上,一直以爲我的單調棧是沒有問題的。
假如我現在有3個數1,2,1,我的[1,3]區間會被統計兩次,也就是由於重複元素作爲最小值的存在,我計算多了。
所以我們需要單調隊列維護半閉半開區間。
具體操作:維護單調遞增棧。 是 左邊第一個小於等於 的數, 是 右邊第一個小於 的數。認爲 是 的最小值。
代碼:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 5e5 + 5 ;
int rk[maxn << 1] , sa[maxn << 1] , height[maxn << 1] ;
int tmp[maxn << 1] , cnt[maxn] ;
char s[maxn] ;
/*struct node
{
int id , x ;
} q[maxn] ;*/
int q[maxn] ;
int L[maxn] , R[maxn] ;
int ans[maxn] ;
void suffixarray(int n , int m)
{
n ++ ;
for(int i = 0 ; i < n * 2 + 5 ; i ++)
rk[i] = sa[i] = height[i] = tmp[i] = 0 ;//開2 倍空間
for(int i = 0 ; i < m ; i ++) cnt[i] = 0 ;
for(int i = 0 ; i < n ; i ++) cnt[rk[i] = s[i]] ++ ;
for(int i = 1 ; i < m ; i ++) cnt[i] += cnt[i - 1] ;
for(int i = 0 ; i < n ; i ++) sa[-- cnt[rk[i]]] = i ;
for(int k = 1 ; k <= n ; k <<= 1)
{
int j = 0 ;
for(int i = 0 ; i < n ; i ++)
{
j = sa[i] - k ;
if(j < 0) j += n ;
tmp[cnt[rk[j]] ++] = j ;
}
sa[tmp[cnt[0] = 0]] = j = 0 ;
for(int i = 1 ; i < n ; i ++)
{
if(rk[tmp[i]] != rk[tmp[i - 1]]
|| rk[tmp[i] + k] != rk[tmp[i - 1] + k])
cnt[++ j] = i ;
sa[tmp[i]] = j ;
}
memcpy(rk , sa , n * sizeof(int)) ;
memcpy(sa , tmp , n * sizeof(int)) ;
if(j >= n - 1) break ;
}
height[0] = 0 ;
for(int i = 0 , k = 0 , j = rk[0] ; i < n - 1 ; i ++ , k ++)
while(~k && s[i] != s[sa[j - 1] + k])
height[j] = k -- , j = rk[sa[j] + 1] ;
}
void solve(int n)
{
ll num = (ll)(n - 1) * n * (n + 1) / 2 ;
n ++ ;
height[n] = 0 ;
/*int l = 1 , r = 0 ;
q[++ r].id = 1 , q[r].x = 1 ;
for(int i = 2 ; i <= n ; i ++)
{
int z = 0 ;
int c = 0 ;
while(l <= r && height[q[r].id] > height[i])
{
ans[q[r].id] += q[r].x ;
ans[q[r].id] += z + c ;
z ++ ;
c += q[r].x ;
r -- ;
}
q[++ r].id = i ;
q[r].x = z + c ;
}
for(int i = 1 ; i <= n ; i ++)
cout << i << ' ' << height[i] << ' ' << ans[i] + 1 << '\n' ;*/
int l = 1 , r = 0 ;
for(int i = 2 ; i <= n ; i ++)
{
while(l <= r && height[q[r]] > height[i])
{
L[i] += L[q[r]] + 1 ;
r -- ;
}
q[++ r] = i ;
}
l = 1 , r = 0 ;
for(int i = n ; i >= 2 ; i --)
{
while(l <= r && height[q[r]] >= height[i])
{
R[i] += R[q[r]] + 1 ;
r -- ;
}
q[++ r] = i ;
}
//for(int i = 1 ; i <= n ; i ++)
//ans[i] = L[i] + R[i] + 1 , cout << i << ' ' << height[i] << ' ' << ans[i] << '\n' ;
for(int i = 2 ; i <= n ; i ++)
num -= 2ll * height[i] * (R[i] + 1) * (L[i] + 1) ;
printf("%lld\n" , num) ;
}
int main()
{
int len ;
//freopen("data.in","r",stdin); //從文件data.in中讀入數據
//freopen("B.out","w",stdout); //輸出的結果存在ZhengJie.out文件中
scanf("%s" , s) ;
len = strlen(s) ;
suffixarray(len , 200) ;
solve(len) ;
//printf("%s" , s) ;
return 0 ;
}