题目要求
所以
后面要减去的部分是两两后缀的,那么做了后缀数组之后就是
后面其实就是里面所有区间的最小值加起来再乘.
这样我们考虑一个值可以作为最小值能够拓展的最左端和最右端,就可以算出这个值对答案的贡献.这个就单调栈就行了.具体见代码.
…mdzz又双叒叕缀写错后缀(chuo)数组了.
CODE
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 5e5+5;
char s[MAXN];
int n, x[MAXN], y[MAXN], c[MAXN], sa[MAXN], rk[MAXN], ht[MAXN];
inline void Get_Sa(int m) {
for(int i = 1; i <= m; ++i) c[i] = 0;
for(int i = 1; i <= n; ++i) ++c[x[i]=s[i]-'a'+1];
for(int i = 2; i <= m; ++i) c[i] += c[i-1];
for(int i = n; i >= 1; --i) sa[c[x[i]]--] = i;
for(int k = 1; k <= n; k<<=1) {
int p = 0;
for(int i = n-k+1; i <= n; ++i) y[++p] = i;
for(int i = 1; i <= n; ++i) if(sa[i]>k) y[++p] = sa[i]-k;
for(int i = 1; i <= m; ++i) c[i] = 0;
for(int i = 1; i <= n; ++i) ++c[x[i]];
for(int i = 2; i <= m; ++i) c[i] += c[i-1];
for(int i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i];
swap(x, y);
x[sa[1]] = p = 1;
for(int i = 2; i <= n; ++i)
x[sa[i]] = (y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k]) ? p : ++p;
if((m=p) == n) break;
}
for(int i = 1; i <= n; ++i) rk[sa[i]] = i;
}
inline void Get_Ht() {
for(int i = 1, k = 0, j; i <= n; ++i) if(rk[i] > 1) {
j = sa[rk[i]-1]; k = k ? k-1 : 0;
while(i+k <= n && j+k <= n && s[i+k] == s[j+k]) ++k;
ht[rk[i]] = k;
}
}
int st[MAXN], top, L[MAXN], R[MAXN];
int main() {
scanf("%s", s+1);
n = strlen(s+1);
Get_Sa(26); Get_Ht();
L[st[top=1] = 1] = 0;
for(int i = 2; i <= n; ++i) {
while(top && ht[st[top]] > ht[i]) R[st[top--]] = i;
L[i] = st[top]; st[++top] = i;
}
while(top) R[st[top--]] = n+1;
LL ans = 1ll * (n-1) * n * (n+1) / 2;
for(int i = 2; i <= n; ++i)
ans -= 2ll * (i-L[i]) * (R[i]-i) * ht[i];
printf("%lld
", ans);
}