zoukankan      html  css  js  c++  java
  • #1413 : Rikka with String 后缀自动机 + 二级差分

    http://hihocoder.com/problemset/problem/1413?sid=1199641

    这题断断续续做了2个多星期吧,一直不会

    设总答案为sum,替换后新加的子串数量为x,失去的是y,那么每个位置的答案就是sum + x[i] - y[i]

    首先可以知道如果把某个位置设置成'#',那么肯定有i * (len - i + 1)个新的不同的子串

    比如是aa#cb,左边有i个选择,右边有len - i + 1个选择,根据组合数学就是i * (len - i + 1)个不同的子串

    然后替换过后,就会有一些原本有的子串被删除了。

    对于每一个状态,可以拓扑出它的mxpos和mipos也就是endpos的两个位置。

    那么对于一个长度是len的子串,是否删除 了这个字符后 在整个字符串中不再出现,可以这样判断

    如果mxpos - len + 1(也就是这个长度是len的子串的最大开始位置),如果这个位置还 < mipos那么如果删除了这个位置

    肯定会丢失这个长度是len的字符串了。可以画个图吧看看

    #include <bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false)
    using namespace std;
    #define inf (0x3f3f3f3f)
    typedef long long int LL;
    const int maxn = 3e5 + 20, N = 26;
    struct SAM {
        int mxCnt[maxn << 1], son[maxn << 1][N], fa[maxn << 1], pos[maxn << 1];
        int flag[maxn << 1][3]; //是否前缀节点
        int mi[maxn << 1], mx[maxn << 1];
        int root, last, DFN, t;
        int create() {
            ++t;
            mxCnt[t] = pos[t] = fa[t] = NULL;
    //        mi[t] = inf, mx[t] = -inf;
            for (int i = 0; i < 3; ++i) flag[t][i] = NULL;
            for (int i = 0; i < N; ++i) son[t][i] = NULL;
            return t;
        }
        void init() {
            ++DFN;
            t = 0, root = 1;
            last = create();
        }
        void addChar(int x, int _pos, int id) { // _pos表示在原串中的位置
            int p = last;
            int np = create();
            last = np;
            mxCnt[np] = mxCnt[p] + 1, pos[np] = _pos, flag[np][id] = DFN; //前缀节点
            for (; p && son[p][x] == NULL; p = fa[p]) son[p][x] = np;
            if (p == NULL) {
                fa[np] = root;
                return;
            }
            int q = son[p][x];
            if (mxCnt[q] == mxCnt[p] + 1) {
                fa[np] = q;
                return;
            }
            int nq = create(); //用来代替q的,默认不是前缀节点
            flag[nq][id] = DFN - 1; //默认不是前缀节点
            pos[nq] = pos[q]; //pos要和q相同
            for (int i = 0; i < N; ++i) son[nq][i] = son[q][i];
            fa[nq] = fa[q], mxCnt[nq] = mxCnt[p] + 1;
            fa[q] = nq, fa[np] = nq;
            for (; p && son[p][x] == q; p = fa[p]) son[p][x] = nq;
        }
        int dp[maxn << 1], in[maxn << 1], que[maxn << 1];
        void topo() { //多次使用不用清空
            for (int i = 2; i <= t; ++i) {
                in[fa[i]]++;
                mi[i] = mx[i] = pos[i];
            }
            int head = 0, tail = 0;
            for (int i = 2; i <= t; ++i) {
                if (in[i] == 0) que[tail++] = i;
            }
            while (head < tail) {
                int cur = que[head++];
                if (cur == root) break;
                mx[fa[cur]] = max(mx[fa[cur]], mx[cur]);
                in[fa[cur]]--;
                if (in[fa[cur]] == 0) que[tail++] = fa[cur];
            }
        }
    } sam;
    LL cnt[maxn], sub[maxn];
    void add(int be, int en, LL val, LL d) {
        cnt[be] += val;
        cnt[en + 1] -= d * (en - be) + val;
        sub[be + 1] += d;
        sub[en + 1] -= d;
    }
    void init(int en) {
        for (int i = 1; i <= en; ++i) {
            sub[i] += sub[i - 1];
            cnt[i] += cnt[i - 1] + sub[i];
        }
    }
    
    char str[maxn];
    
    void work() {
        int len;
        cin >> len;
        sam.init();
        scanf("%s", str + 1);
        LL ans = 0;
        for (int i = 1; str[i]; ++i) {
            sam.addChar(str[i] - 'a', i, 0);
        }
        sam.topo();
    //    int fuck = 5;
    //    printf("%d
    ", sam.mx[fuck + 1]);
        for (int i = 2; i <= sam.t; ++i) {
            ans += sam.mxCnt[i] - sam.mxCnt[sam.fa[i]];
            if (sam.mx[i] - sam.mxCnt[i] + 1 <= sam.mi[i]) {
                int be = sam.mx[i] - sam.mxCnt[i] + 1;
                int en = min(sam.mx[i] - sam.mxCnt[sam.fa[i]], sam.mi[i]);
                int len = en - be + 1;
    //            printf("%d %d
    ", be, en);
                add(be, en, 1, 1);
                be = en + 1;
                en = sam.mi[i];  //还有一段,要全部加上len个
    //            printf("%d %d
    
    ", be, en);
    
                if (be <= en) add(be, en, len, 0);
    
            }
        }
        init(len);
    //    printf("%d
    ", cnt[4]);
        for (int i = 1; i <= len; ++i) {
            printf("%lld ", ans + 1LL * (i) * (len - i + 1) - cnt[i]);
        }
    
    }
    
    int main() {
    #ifdef local
        freopen("data.txt", "r", stdin);
    //    freopen("data.txt", "w", stdout);
    #endif
        work();
        return 0;
    }
    View Code
  • 相关阅读:
    MyBatis学习总结_17_Mybatis分页插件PageHelper
    MyBatis学习总结_16_Mybatis使用的几个建议
    MyBatis学习总结_15_定制Mybatis自动代码生成的maven插件
    MyBatis学习总结_14_Mybatis使用技巧总结
    MyBatis学习总结_13_Mybatis查询之resultMap和resultType区别
    Git实战之Git创建版本库
    Oracle数据库之日期函数
    Oracle数据库之单表查询
    版本发布后测试人员需要做的工作
    oracle数据库之数据插入、修改和删除
  • 原文地址:https://www.cnblogs.com/liuweimingcprogram/p/7610130.html
Copyright © 2011-2022 走看看