1. 程式人生 > >P4248 [AHOI2013]差異

P4248 [AHOI2013]差異

\(\color{#0066ff}{題目描述}\)

給定一個長度為 \(n\) 的字串 \(S\),令 \(T_i\)​ 表示它從第 \(i\) 個字元開始的字尾。求

\(\displaystyle \sum_{1\leqslant i<j\leqslant n}\text{len}(T_i)+\text{len}(T_j)-2\times\text{lcp}(T_i,T_j)\)

其中,\(\text{len}(a)\) 表示字串 \(a\) 的長度,\(\text{lcp}(a,b)\) 表示字串 \(a\) 和字串 \(b\) 的最長公共字首。

\(\color{#0066ff}{輸入格式}\)

一行,一個字串 \(S\)

\(\color{#0066ff}{輸出格式}\)

一行,一個整數,表示所求值。

\(\color{#0066ff}{輸入樣例}\)

cacao

\(\color{#0066ff}{輸出樣例}\)

54

\(\color{#0066ff}{資料範圍與提示}\)

對於 100% 的資料,保證 \(2\leqslant n\leqslant 500000\),且均為小寫字母

\(\color{#0066ff}{題解}\)

定義H為排名為i-1和i的字串的LCP

我們有\(H[i] \geq H[i-1] - 1\)

還有\(LCP(i,j)=min(LCP(i,k),LCP(k,j))\)

\(\begin{aligned}LCP(i,j)=\min_{i+1\leq k \leq j}(H[k]) \end{aligned}\)

這裡就不證明了其實我不會

所以把大的\(\Sigma\)拆開

前兩部分可以直接統計

後面的LCP可以先求出H,然後單調棧求出每個數作為最小值對於區間的貢獻

不看LL見祖宗

#include <bits/stdc++.h>

typedef long long LL;

const int maxn = 5e5 + 100;
const int inf = 0x7fffffff;

int n ,m;
int H[maxn], c[maxn], x[maxn], y[maxn], sa[maxn], rk[maxn];
LL l[maxn], r[maxn];
char s[maxn];
LL ans1, ans2, ans3;

class stack {
private:
    int st[maxn], tp;
public:
    void pop() { tp--; }
    int top() { return st[tp]; }
    void push(int x) { st[++tp] = x; }
    void clr() { memset(st, 0, sizeof st); }
}S;

LL getans(LL t) {
    return (t * (t + 1LL)) >> 1LL;
}

void predoit() {
    for(int i = 1; i <= n; i++) c[x[i] = s[i]]++;
    for(int i = 1; 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 num = 0;
        for(int i = n - k + 1; i <= n; i++) y[++num] = i;
        for(int i = 1; i <= n; i++) if(sa[i] > k) y[++num] = 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 = 1; i <= m; i++) c[i] += c[i - 1];
        for(int i = n; i >= 1; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
        std::swap(x ,y);
        x[sa[1]] = 1, num = 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])? num : ++num;
        if(num == n) break;
        m = num;
    }
    for(int i = 1; i <= n; i++) rk[i] = x[i];
    int h = 0;
    for(int i = 1; i <= n; i++) {
        if(rk[i] == 1) continue;
        if(h) h--;
        int j = sa[rk[i] - 1];
        while(j + h <= n && s[i + h] == s[j + h]) h++;
        H[rk[i]] = h;
    }
}

void work_contribute() {
    S.push(1);
    H[1] = -inf;
    for(int i = 2; i <= n; i++) {
        while(H[S.top()] >= H[i]) S.pop();
        l[i] = S.top();
        S.push(i);
    }
    S.clr();
    S.push(n + 1);
    H[n + 1] = -inf;
    for(int i = n; i >= 2; i--) {
        while(H[S.top()] > H[i]) S.pop();
        r[i] = S.top();
        S.push(i);
    }
    for(LL i = 2LL; i <= n; i++) ans3 += (i - l[i]) * (r[i] - i) * H[i];
}

void query() {
    ans3 <<= 1LL;
    for(LL i = n; i >= 1; i--) ans1 += i * (i - 1LL);
    for(int i = n - 1; i >= 1; i--) ans2 += getans(i);
    printf("%lld", ans1 - ans3 + ans2);
}

int main() {
    scanf("%s", s + 1);
    n = strlen(s + 1), m = 122;
    predoit();
    work_contribute();
    query();
    return 0;
}