zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    求100-999之间所有的水仙花数
    验证用户密码程序
    【bzoj2002】[Hnoi2010]Bounce 弹飞绵羊 分块/LCT
    【bzoj1070】[SCOI2007]修车 最小费用流
    【bzoj3669】[Noi2014]魔法森林 Kruskal+LCT
    【bzoj3668】[Noi2014]起床困难综合症 贪心
    【bzoj1391】[Ceoi2008]order 网络流最小割
    【bzoj4873】[Shoi2017]寿司餐厅 最大权闭合图
    【bzoj1180】[CROATIAN2009]OTOCI LCT
    【bzoj3282】Tree LCT
  • 原文地址:https://www.cnblogs.com/2aptx4869/p/14174815.html
Copyright © 2011-2022 走看看