[AHOI2013]差异

Description

BZOJ3238
Luogu4248
(sum_{1le i < jle 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
", ans);
    return 0;
}

Note

一定要注意将字符转化为数字的时候要和这一位没有字符((0))区分开。

原文地址:https://www.cnblogs.com/wyxwyx/p/bzoj3238.html