#1413 : Rikka with String 后缀自动机 + 二级差分
生活随笔
收集整理的這篇文章主要介紹了
#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\n", 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\n", be, en);add(be, en, 1, 1);be = en + 1;en = sam.mi[i]; //還有一段,要全部加上len個 // printf("%d %d\n\n", be, en);if (be <= en) add(be, en, len, 0);}}init(len); // printf("%d\n", cnt[4]);for (int i = 1; i <= len; ++i) {printf("%lld ", ans + 1LL * (i) * (len - i + 1) - cnt[i]);}}int main() { #ifdef localfreopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endifwork();return 0; } View Code?
轉載于:https://www.cnblogs.com/liuweimingcprogram/p/7610130.html
總結
以上是生活随笔為你收集整理的#1413 : Rikka with String 后缀自动机 + 二级差分的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 获取url后的指定参数
- 下一篇: STM32F4 编程手册学习1_编程模型