#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
原文地址:https://www.cnblogs.com/liuweimingcprogram/p/7610130.html