• P4248 [AHOI2013]差异


    (color{#0066ff}{题目描述})

    给定一个长度为 (n) 的字符串 (S),令 (T_i)​ 表示它从第 (i) 个字符开始的后缀。求

    (displaystyle sum_{1leqslant i<jleqslant n} ext{len}(T_i)+ ext{len}(T_j)-2 imes ext{lcp}(T_i,T_j))

    其中,( ext{len}(a)) 表示字符串 (a) 的长度,( ext{lcp}(a,b)) 表示字符串 (a) 和字符串 (b) 的最长公共前缀。

    (color{#0066ff}{输入格式})

    一行,一个字符串 (S)

    (color{#0066ff}{输出格式})

    一行,一个整数,表示所求值。

    (color{#0066ff}{输入样例})

    cacao
    

    (color{#0066ff}{输出样例})

    54
    

    (color{#0066ff}{数据范围与提示})

    对于 100% 的数据,保证 (2leqslant nleqslant 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)))

    (egin{aligned}LCP(i,j)=min_{i+1leq 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;
    }
    
  • 相关阅读:
    直方图
    Netty学习摘记 —— 初步认识Netty核心组件
    打败算法 —— 圆圈中最后剩下的数字
    打败算法 —— 单词的压缩编码
    大数据生态圈 —— 关于实时流处理的单节点伪分布式环境搭建
    打败算法 —— 按摩师
    打败算法 —— 最长公共子序列
    Spark学习摘记 —— Pair RDD行动操作API归纳
    Spark学习摘记 —— Pair RDD转化操作API归纳
    Spark学习摘记 —— RDD行动操作API归纳
  • 原文地址:https://www.cnblogs.com/olinr/p/10161093.html
Copyright © 2020-2023  润新知