Description
Input
一行,一个字符串S
Output
一行,一个整数,表示所求值
Sample Input
cacao
Sample Output
54
HINT
2<=N<=500000,S由小写英文字母组成
Source
重点在如何求 ∑i<jLCP(i,j) 。
也就是求所有区间最小值之和。
定义 f[i] 为1~i的答案,可以维护一个单调不下降的栈,然后用栈顶元素更新答案,详见代码。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int SZ = 1000010;
int sa[SZ],n,rank[SZ],tmp[SZ],lcp[SZ],k = 1;
bool cmp_sa(int i,int j)
{
if(rank[i] != rank[j]) return rank[i] < rank[j];
else
{
int x = i + k <= n ? rank[i + k] : -1;
int y = j + k <= n ? rank[j + k] : -1;
return x < y;
}
}
void get_sa(char s[])
{
for(int i = 0;i <= n;i ++)
{
sa[i] = i;
rank[i] = i < n ? s[i] : -1;
}
for(k = 1;k <= n;k <<= 1)
{
sort(sa,sa + 1 + n,cmp_sa);
tmp[sa[0]] = 0;
for(int i = 1;i <= n;i ++)
tmp[sa[i]] = tmp[sa[i - 1]] + (cmp_sa(sa[i - 1],sa[i]) ? 1 : 0);
for(int i = 0;i <= n;i ++)
rank[i] = tmp[i];
}
}
void get_lcp(char s[])
{
int h = 0;
lcp[0] = 0;
for(int i = 0;i <= n;i ++)
rank[sa[i]] = i;
for(int i = 0;i < n;i ++)
{
int j = sa[rank[i] - 1];
if(h) h --;
while(i + h < n && j + h < n)
{
if(s[i + h] == s[j + h]) h ++;
else break;
}
lcp[rank[i] - 1] = h;
}
}
char s[SZ];
struct haha{
int id,h;
}S[SZ];
LL f[SZ];
LL ask()
{
LL ans = 0;
for(int i = 1;i <= n;i ++)
ans += (LL)i * (LL)(n - i) + ((LL)(n - i) * (LL)(i + 1 + n) >> 1ll);
int top = 0;
LL tot = 0;
S[++ top] = (haha){0,lcp[0]};
for(int i = 1;i < n;i ++)
{
while(top && S[top].h > lcp[i]) top --;
// cout<<top<<endl;
f[i] = f[S[top].id] + ((LL)(i - S[top].id)) * (LL)lcp[i];
tot += f[i];
S[++ top] = (haha){i,lcp[i]};
}
return ans - tot * 2ll;
}
int main()
{
scanf("%s",s);
n = strlen(s);
get_sa(s);
get_lcp(s);
printf("%lld\n",ask());
return 0;
}