• [BZOJ3238][Ahoi2013]差异


    3238: [Ahoi2013]差异

    Time Limit: 20 Sec  Memory Limit: 512 MB
    Submit: 3394  Solved: 1542
    [Submit][Status][Discuss]

    Description

    Input

    一行,一个字符串S

    Output

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

    Sample Input

    cacao

    Sample Output


    54

    HINT



    2<=N<=500000,S由小写英文字母组成

    先把答案加的那部分弄出来,可以$O(1)$计算。。

    然后剩下的部分可以通过建反串的后缀自动机,那么$Parent$树就是原串的后缀树。。

    然后树形DP即可

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 500000 + 10;
    struct State {
        int len, link, tot;
        int son[26];
    }st[maxn * 2];
    int cnt, last;
    void sam_init() {
        cnt = last = 0;
        st[0].len = 0;
        st[0].link = -1;
        st[0].tot = 0;
        memset(st[0].son, 0, sizeof(st[0].son));
    }
    void sam_extend(char c) {
        int cur = ++cnt, idx = c - 'a';
        st[cur].len = st[last].len + 1;
        memset(st[cur].son, 0, sizeof(st[cur].son));
        st[cur].tot = 1;
        int p;
        for (p = last; p != -1 && !st[p].son[idx]; p = st[p].link)
            st[p].son[idx] = cur;
        if (p == -1) st[cur].link = 0;
        else {
            int q = st[p].son[idx];
            if (st[p].len + 1 == st[q].len) st[cur].link = q;
            else {
                int clone = ++cnt;
                st[clone].len = st[p].len + 1;
                memcpy(st[clone].son, st[q].son, sizeof(st[q].son));
                st[clone].link = st[q].link;
                st[clone].tot = 0;
                for (; p != -1 && st[p].son[idx] == q; p = st[p].link)
                    st[p].son[idx] = clone;
                st[q].link = st[cur].link = clone;
            }
        }
        last = cur;
    }
    char S[maxn];
    int len;
    struct Edge {
        int to, next;
        Edge() {}
        Edge(int _t, int _n) : to(_t), next(_n) {}
    }e[maxn * 2];
    int fir[maxn * 2] = { 0 }, ecnt = 0;
    inline void add(int u, int v) {
        e[++ecnt] = Edge(v, fir[u]); fir[u] = ecnt;
    }
    void build() {
        for (int i = 1; i <= cnt; i++)
            add(st[i].link, i);
    }
    long long ans;
    void dfs(int u) {
        for (int v, i = fir[u]; i; i = e[i].next) {
            v = e[i].to;
            dfs(v);
            ans -= 2LL * st[u].len * st[u].tot * st[v].tot;
            st[u].tot += st[v].tot;
        }
    }
    int main() {
        scanf("%s", S);
        sam_init();
        len = strlen(S);
        for (int i = 0; i < len; i++)
            sam_extend(S[len - i - 1]);
        build();
        ans = (long long)(len + 1) * len * (len - 1) / 2;
        dfs(0);
        printf("%lld
    ", ans);
        return 0;
    }

     时隔两年来更新

    前面两项可以$O(1)$计算

    主要是计算后面一项

    注意到$sa$数组是一个$1$到$n$的全排列且$lcp(T_i,T_j)=lcp(T_j,T_i)$

    因此$sum_{1le i<jle n}lcp(T_i,T_j)=sum_{1le i<jle n}lcp(T_{sa[i]},T_{sa[j]})$

    而$lcp(T_{sa[i]},T_{sa[j]})=min_{i+1le kle j}(height[k])$

    因此原式可变形为$sum_{2le ile jle n}min_{ile kle j}(height[k])$

    可以考虑计算每个$height[i]$作为最小值时被多少个区间包含

    显然可以用单调栈维护

    注意一下相等数值之间的影响

    时间复杂度$O(nlogn)$

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int maxn = 500000 + 10;
    char s[maxn];
    int n, m;
    int sa[maxn], rank[maxn], height[maxn];
    int tax[maxn], tp[maxn];
    void qsort(){
        for(int i = 1; i <= m; i++) tax[i] = 0;
        for(int i = 1; i <= n; i++) tax[rank[i]]++;
        for(int i = 2; i <= m; i++) tax[i] += tax[i - 1];
        for(int i = n; i; i--) sa[tax[rank[tp[i]]]--] = tp[i];
    }
    bool cmp(int *a, int l, int r, int w){
        return a[l] == a[r] && a[l + w] == a[r + w];
    }
    void suffix_sort(){
        m = 128;
        for(int i = 1; i <= n; i++) rank[i] = s[i];
        for(int i = 1; i <= n; i++) tp[i] = i;
        qsort();
        for(int k = 1, p = 0; p < n; m = p, k <<= 1){
            p = 0;
            for(int i = 1; i <= k; i++) tp[++p] = n - k + i;
            for(int i = 1; i <= n; i++) if(sa[i] > k) tp[++p] = sa[i] - k;
            qsort();
            swap(tp, rank);
            p = rank[sa[1]] = 1;
            for(int i = 2; i <= n; i++)
                rank[sa[i]] = cmp(tp, sa[i - 1], sa[i], k) ? p : ++p;
        }
        for(int i = 1, j, k = 0; i <= n; height[rank[i++]] = k)
            for(k ? k-- : 0, j = sa[rank[i] - 1]; s[i + k] == s[j + k]; k++);
    }
    int f[maxn], g[maxn], sta[maxn], top;
    void calc(){
        ll ans = (ll)n * (n - 1) * (n + 1) / 2;
        f[2] = 1;
        top = 0;
        for(int i = 2; i <= n; i++){
            while(top && height[sta[top]] > height[i]) top--;
            if(!top) f[i] = i - 1;
            else f[i] = i - sta[top];
            sta[++top] = i;
        }
        g[n] = 1;
        top = 0;
        for(int i = n; i >= 2; i--){
            while(top && height[sta[top]] >= height[i]) top--;
            if(!top) g[i] = n - i + 1;
            else g[i] = sta[top] - i;
            sta[++top] = i;
        }
        for(int i = 2; i <= n; i++)
            ans -= (ll)2 * f[i] * g[i] * height[i];    
        printf("%lld
    ", ans);
    }
    int main(){
        scanf("%s", s + 1);
        n = strlen(s + 1);
        suffix_sort();
        calc();
        return 0;
    }
  • 相关阅读:
    算法演示工具
    1198:逆波兰表达式
    1315:【例4.5】集合的划分
    1192:放苹果
    1191:流感传染
    1354括弧匹配检验
    1331【例1-2】后缀表达式的值
    1307高精度乘法
    1162字符串逆序
    1161转进制
  • 原文地址:https://www.cnblogs.com/ruoruoruo/p/7604164.html
Copyright © 2020-2023  润新知