1. 程式人生 > >【清華集訓2017模擬12.10】迴文串(迴文樹+樹鏈剖分)

【清華集訓2017模擬12.10】迴文串(迴文樹+樹鏈剖分)

Description:

NYG 很喜歡研究迴文串問題,有一天他想到了這樣一個問題:
給出一個字串 S,現在有 4 種操作:
• addl c :在當前字串的左端加入字元 c;
• addr c :在當前字串的右端加入字元 c;
• transl l 1 r 1 l 2 r 2 :取出 S 的兩個子串 S[l 1 …r 1 ],S[l 2 …r 2 ],現在 NYG想把前一個字串變換為後一個字串,每次操作他可以在前一個字串的左端插入或刪除一個字元,保證 NYG 會使用盡量少的步數進行操作,你需要輸出整個操作的優美度。
• transr l 1 r 1 l 2 r 2 :取出 S 的兩個子串 S[l 1 …r 1 ],S[l 2 …r 2 ],現在 NYG想把前一個字串變換為後一個字串,每次操作他可以在前一個字串的右端插入或刪除一個字元,保證 NYG 會使用盡量少的步數進行操作,你需要輸出整個操作的優美度。
設字串 S 長為 n,且從 1 開始標號,那麼 nyg 這樣定義一次變換的優美度 p:
定義 S[i…j] 是好的當且僅當 S[i…j] 是迴文的而且在變換中出現過.
這裡寫圖片描述

例如 S = abaabac,現在執行 transr 1 5 4 7,其變換過程為:
abaab -> abaa -> aba -> abac
注意上述四個串均視為在此次變換中出現,其中 aba 為迴文串,且S[1…3] = S[4…6] = aba,故此次變換的優美度為 6。
由於 NYG 還要忙著出題,這個任務就交給你了。

1 <=n <= 10^5

題解:

這題真的很牛啊,碼了2h,調了2h。

看到迴文串肯定想到迴文樹了。

迴文樹有一個非常重要的性質:
正串和反串的迴文樹的形態是一樣的,這個性質是做這題的基礎。

這題沒有強制線上,可以先把整個串擼出來。

接著正著做一遍迴文樹,反串丟上去跑求對應的編號。

接著考慮每次加字元,就相當於把它對應的點的編號到根的路徑的點的出現次數加1。

這樣當然是錯誤的,我就因為這個卡了1h。

一個點代表的長度如果是len,但是我當前整個串的長度都<len,那麼,加進去的串必然是多了的,有些串現在沒有出現,在以後才會出現,只是因為我是對整個串建迴文樹而已。

所以要往fail鏈上調整,直到當前點代表的長度小於當前串的長度。

對於查詢,可以近似的看作是查兩個點的路徑上的所有點的出現次數*長度,當然,這兩個點也是需要調整長度後的點。

注意如果說這兩個點的lca在串中代表的串再往後一個也是相同的,那麼我在變換中必然不會到達lca,所以這種情況要特判。

對於答案的統計可以用樹鏈剖分+線段樹維護,對於調整長度的操作可以用樹鏈剖分+二分維護,那麼這題的複雜度是O(n(logn)2)

實際上達不到。

Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;

const int N = 4e5 + 5;

int n, Q, st, en;
int str[N];

int tt[N], next[N], final[N], tpt;
void link(int x, int y) {next[++ tpt] = final[x], tt[tpt] = y, final[x] = tpt;} 

int dep[N], top[N], w[N], son[N], siz[N], fa[N], tw;
struct tree {
    ll bz, slen, sum;
} t[N * 10];

int wl[N], num[N];

void mkt(int i, int x, int y) {
    if(x == y) t[i].slen = wl[x]; else {
        int m = x + y >> 1;
        mkt(i + i, x, m); mkt(i + i + 1, m + 1, y);
        t[i].slen = t[i + i].slen + t[i + i + 1].slen;
    }
}

void down(int i) {
    if(t[i].bz) {
        t[i + i].bz += t[i].bz;
        t[i + i].sum += t[i].bz * t[i + i].slen;
        t[i + i + 1].bz += t[i].bz;
        t[i + i + 1].sum += t[i].bz * t[i + i + 1].slen;
        t[i].bz = 0;
    }
}

void cadd(int i, int x, int y, int l, int r) {
    if(x == l && y == r) {
        t[i].bz ++;
        t[i].sum += t[i].slen;
    } else {
        down(i);
        int m = x + y >> 1;
        if(r <= m) cadd(i + i, x, m, l, r); else
        if(l > m) cadd(i + i + 1, m + 1, y, l, r); else
        cadd(i + i, x, m, l, m), cadd(i + i + 1, m + 1, y, m + 1, r);
        t[i].sum = t[i + i].sum + t[i + i + 1].sum;
    }
}

ll find(int i, int x, int y, int l, int r) {
    if(x == l && y == r) return t[i].sum;
    down(i);
    int m = x + y >> 1;
    if(r <= m) return find(i + i, x, m, l, r);
    if(l > m) return find(i + i + 1, m + 1, y, l, r);
    return find(i + i, x, m, l, m) + find(i + i + 1, m + 1, y, m + 1, r);
}

int lca(int x, int y) {
    while(top[x] != top[y])
        if(dep[top[x]] > dep[top[y]])
            x = fa[top[x]]; else y = fa[top[y]];
    return dep[x] < dep[y] ? x : y;
}

struct hwt {
    int fail[N], to[N][26], len[N], tot, last, n, lat[N];
    void First() {
        tot = 1;
        len[1] = -1; len[0] = 0;
        fail[0] = 1;
        last = 0;
        str[0] = -1;
    }
    int Getfail(int p) {
        while(str[n - len[p] - 1] != str[n]) p = fail[p];
        return p;
    }
    void add(char c) {
        n ++;
        int cur = Getfail(last);
        if(!to[cur][c]) {
            int p = ++ tot;
            len[p] = len[cur] + 2;
            fail[p] = to[Getfail(fail[cur])][c];
            to[cur][c] = p;
        }
        last = to[cur][c];
        lat[n] = last;
    }
    void dg1(int x) {
        siz[x] = 1;
        for(int i = final[x]; i; i = next[i]) {
            int y = tt[i];
            fa[y] = x;
            dep[y] = dep[x] + 1;
            dg1(y);
            siz[x] += siz[y];
            if(siz[y] > siz[son[x]]) son[x] = y;
        }
    }
    void dg2(int x) {
        w[x] = ++ tw;
        if(son[x]) top[son[x]] = top[x], dg2(son[x]);
        for(int i = final[x]; i; i = next[i]) {
            int y = tt[i]; if(y == son[x]) continue;
            top[y] = y; dg2(y);
        }
    }
    void pou() {
        fo(i, 1, tot) if(lat[i] == 0) lat[i] = 1;
        len[1] = 0;

        fo(i, 2, tot) {
            if(fail[i] == 0) fail[i] = 1;
            link(fail[i], i);
        }
        dep[1] = 1; dg1(1);
        top[1] = 1; dg2(1);

        fo(i, 1, tot) num[w[i]] = i;

        fo(i, 1, tot) wl[w[i]] = len[i];
        mkt(1, 1, tot);
    }
    ll sum(int x, int y) {
        ll s = 0;
        while(top[x] != top[y])
            s += find(1, 1, tot, w[top[x]], w[x]), x = fa[top[x]];
        s += find(1, 1, tot, w[y], w[x]);
        return s;
    }
    int tiao(int z, int mi) {
        while(len[top[z]] > mi) z = fa[top[z]];
        int ans = 0;
        for(int l = w[top[z]], r = w[z]; l <= r; ) {
            int m = l + r >> 1;
            if(len[num[m]] <= mi)
                ans = m, l = m + 1; else r = m - 1;
        }
        return num[ans];
    }
} zt, ft;

void addg(int x) {
    while(x) cadd(1, 1, zt.tot, w[top[x]], w[x]), x = fa[top[x]];
}

struct Ask {
    char s[10];
    int l1, r1, l2, r2, x, c;
    void read() {
        scanf("%s", s);
        if(s[0] == 'a') scanf("%d", &c); else scanf("%d %d %d %d", &l1, &r1, &l2, &r2);
        if(s[0] == 'a')
            if(s[3] == 'l') str[-- st] = c, x = st; else str[++ en] = c, x = en;
    }
    void Zhuo() {
        if(s[0] == 't') {
            l1 += st - 1; r1 += st - 1; l2 += st - 1; r2 += st - 1;
            if(s[5] == 'l') {
                int x = zt.lat[r1], y = zt.lat[r2];
                x = zt.tiao(x, r1 - l1 + 1);
                y = zt.tiao(y, r2 - l2 + 1);
                int z = lca(x, y);
                int mi = min(r1 - l1 + 1, r2 - l2 + 1);
                ll ans = zt.sum(x, z) + zt.sum(y, z) - zt.sum(z, z);
                if(zt.len[z] < mi && str[r1 - zt.len[z]] == str[r2 - zt.len[z]]) ans -= zt.sum(z, z);
                printf("%lld\n", ans);
            } else {
                int x = ft.lat[l1], y = ft.lat[l2];
                x = zt.tiao(x, r1 - l1 + 1);
                y = zt.tiao(y, r2 - l2 + 1);
                int z = lca(x, y);
                int mi = min(r1 - l1 + 1, r2 - l2 + 1);
                ll ans = zt.sum(x, z) + zt.sum(y, z) - zt.sum(z, z);
                if(zt.len[z] < mi && str[l1 + zt.len[z]] == str[l2 + zt.len[z]]) ans -= zt.sum(z, z);
                printf("%lld\n", ans);
            }
        } else {
            if(s[3] == 'l') {
                st --;
                addg(zt.tiao(ft.lat[st], en - st + 1));
            } else {
                en ++;
                addg(zt.tiao(zt.lat[en], en - st + 1));
            }
        }
    }
} aq[N];

void Init() {
    scanf("%d %d", &n, &Q);
    st = 1e5 + 1, en = 1e5 + n;
    fo(i, st, en) scanf("%d", &str[i]);
    fo(i, 1, Q) aq[i].read();
    fo(i, 1, Q) aq[i].x -= st - 1;
    fo(i, 1, en - st + 1) str[i] = str[i + st - 1];
    en = en - st + 1;
}

void Build() {
    zt.First(); ft.First();
    fo(i, 1, en) zt.add(str[i]);
    fo(i, 1, en / 2) swap(str[i], str[en - i + 1]);
    int x = 1, p = 0;
    fo(i, 1, en) {
        p ++;
        while(str[p] != str[p - zt.len[x] - 1]) x = zt.fail[x];
        x = zt.to[x][str[i]];
        ft.lat[en - i + 1] = x;
    }
    fo(i, 1, en) if(!ft.lat[i]) ft.lat[i] = 1;
    fo(i, 1, en / 2) swap(str[i], str[en - i + 1]);
    zt.pou();
}

void End() {
    st = 1e5 + 1 - st + 1; en = st - 1;
    fo(i, 1, n) addg(zt.tiao(zt.lat[++ en], i));
    fo(i, 1, Q) aq[i].Zhuo();
}

int main() {
    freopen("string.in", "r", stdin);
    freopen("string.out", "w", stdout);
    Init();
    Build();
    End();
}