[AHOI2013]差异
Description
BZOJ3238
Luogu4248
求\(\sum_{1\le i < j\le n}(len(suf_i)+len(suf_j)-2lcp(suf_i,suf_j))\)。
Solution
看到\(lcp\)就直接上后缀数组。对于求和的前两项推公式直接\(O(1)\)出,最后一项依次计算每个\(h\)的贡献。具体来说就是降序排序h
后,计算有多少对的询问的答案是\(h_i\),这个可以用并查集搞。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N = 5e5 + 10;
int sa[N], rnk[N], tmp[N], tax[N], h[N];
int n, m, a[N], p[N];
char s[N];
int fa[N], sz[N];
void rsort() {
for (int i = 1; i <= m; ++i) tax[i] = 0;
for (int i = 1; i <= n; ++i) ++tax[rnk[i]];
for (int i = 1; i <= m; ++i) tax[i] += tax[i-1];
for (int i = n; i >= 1; --i) sa[tax[rnk[tmp[i]]]--] = tmp[i];
}
void ssort() {
for (int i = 1; i <= n; ++i) rnk[i] = a[i], tmp[i] = i;
m = 30;
rsort();
for (int w = 1, p = 0; p < n; w <<= 1) {
p = 0;
for (int i = 1; i <= w; ++i) tmp[++p] = n-w+i;
for (int i = 1; i <= n; ++i) if (sa[i] > w) tmp[++p] = sa[i]-w;
rsort();
std::swap(rnk, tmp);
rnk[sa[1]] = p = 1;
for (int i = 2; i <= n; ++i) {
rnk[sa[i]] = (tmp[sa[i]] == tmp[sa[i-1]] && tmp[sa[i]+w] == tmp[sa[i-1]+w]) ? p : ++p;
}
m = p;
}
for (int i = 1, k = 0; i <= n; ++i) {
if (rnk[i] != 1) while (a[i+k] == a[sa[rnk[i]-1]+k]) ++k;
else k = 0;
h[rnk[i]] = k;
if (k) --k;
}
}
int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); }
long long merge(int x, int y) {
int fx = find(x), fy = find(y);
if (fx == fy) return 0;
long long ans = (long long)(sz[fx]) * (sz[fy]);
fa[fy] = fx;
sz[fx] += sz[fy];
return ans;
}
bool cmp(int a, int b) {
return h[a] > h[b];
}
int main() {
scanf("%s", s);
n = strlen(s);
for (int i = 0; i < n; ++i) a[i+1] = s[i]-'a'+1;
ssort();
for (int i = 1; i <= n; ++i) {
fa[i] = i;
sz[i] = 1;
p[i] = i;
}
std::sort(p+1, p+n+1, cmp);
long long ans = (long long)(n+1)*n*(n-1)/2;
for (int i = 1; i <= n; ++i) {
ans = ans - merge(p[i], p[i]-1)*h[p[i]]*2;
}
printf("%lld\n", ans);
return 0;
}
Note
一定要注意将字符转化为数字的时候要和这一位没有字符(\(0\))区分开。