• P4248 [AHOI2013]差异


    P4248 [AHOI2013]差异

    题面

    传送门

    题解

    把式子拆了, 成了两部分,

    (sum len(T) - 2sum_{1≤i<j≤n} lca(T_i, T_j))

    第一部分就是长度 n * (n - 1) * (n + 1) >> 1

    第二部分是所有字串lca长的和的两倍, sam求一下就好了, (算lca, 你反转一下, 不就是等价类的minlen吗?)

    注意对于(T_i, T_j)他们共同的endposs可能是新的复制点nq

    但是按照rk顺序, T_i or T_j 其中之一已经并到了 endposs nq中了, 直接算即可

    我个憨憨, 直接在 endposs nq((T_i, T_j)) 中算的贡献, 那么这个endposs在转移到下一个endposs fa[nq]((T_i, T_j))的时候贡献又被算了一次

    (T_i, T_j) 被算了多次, 且每次被计算的贡献是 endposs的长度 答案肯定是错的

    要在$ T_i or T_j $其中一方进入 endposs, 一方没进入 endposs 的时候算 (T_i ,T_j) 的贡献才是真正的贡献

    struct SAM { //不管是不是多组数据都调用init
        static const int N = 5e5 + 5, M = 26, C = 'a';
        struct node { int fa, len, ne[M]; } tr[N << 1];
        int sz, las, len, c[N], rk[N << 1], cnt[N << 1];//(i~len)有cnt[i]个字母a[i]
        int sum[N << 1]; //排名为i的节点为头包含的字串数量
        int ans[N << 1], f[N << 1];
        void init() {
            rep(i, 1, sz)
                tr[i].len = tr[i].fa = c[i] = 0, memset(tr[i].ne, 0, sizeof tr[i].ne);
            sz = las = 1;
        }
        void add(int ch) {
            int p = las, cur = las = ++sz;
            tr[cur].len = tr[p].len + 1; ++cnt[cur];
            for (; p && !tr[p].ne[ch]; p = tr[p].fa) tr[p].ne[ch] = cur;
            if (p == 0) { tr[cur].fa = 1; return; }
            int q = tr[p].ne[ch];
            if (tr[q].len == tr[p].len + 1) { tr[cur].fa = q; return; }
            int nq = ++sz; tr[nq] = tr[q]; tr[nq].len = tr[p].len + 1;
            for (; p && tr[p].ne[ch] == q; p = tr[p].fa) tr[p].ne[ch] = nq;
            tr[q].fa = tr[cur].fa = nq;
        }
        void build(char* s) {
            for (int& i = len; s[i]; ++i) add(s[i] - C);
        }
        void sort() {
            rep(i, 1, sz) c[i] = 0;
            rep(i, 1, sz) ++c[tr[i].len];
            rep(i, 1, len) c[i] += c[i - 1];
            rep(i, 1, sz) rk[c[tr[i].len]--] = i;
        }
        ll solve(char* s, int n) {
            ll ans = (n - 1ll) * (n + 1ll) * n >> 1;
            per(i, sz, 1) ans -= (ll)cnt[tr[rk[i]].fa] * cnt[rk[i]] * tr[tr[rk[i]].fa].len << 1, cnt[tr[rk[i]].fa] += cnt[rk[i]];
            return ans;
        }
    } sam;
    
    const int N = 5e5 + 5, inf = 0x3f3f3f3f;
    
    int n, m, _, k;
    char s[N];
    
    int main() {
        sam.init(); cin >> s; m = strlen(s); reverse(s, s + m); sam.build(s);
        sam.sort(); cout << sam.solve(s, m);
        return 0;
    }
    
  • 相关阅读:
    (Java实现) 洛谷 P1603 斯诺登的密码
    (Java实现) 洛谷 P1036 选数
    (Java实现) 洛谷 P1036 选数
    (Java实现) 洛谷 P1012 拼数
    (Java实现) 洛谷 P1012 拼数
    (Java实现) 洛谷 P1028 数的计算
    (Java实现) 洛谷 P1028 数的计算
    (Java实现) 洛谷 P1553 数字反转(升级版)
    8.4 确定两个日期之间的月份数或年数
    (Java实现) 洛谷 P1553 数字反转(升级版)
  • 原文地址:https://www.cnblogs.com/2aptx4869/p/14174815.html
Copyright © 2020-2023  润新知