日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

#1413 : Rikka with String 后缀自动机 + 二级差分

發布時間:2025/3/20 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 #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 后缀自动机 + 二级差分的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。