【清华集训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,但是我当前整个串的长度都 < <script type="math/tex" id="MathJax-Element-149"><</script>len,那么,加进去的串必然是多了的,有些串现在没有出现,在以后才会出现,只是因为我是对整个串建回文树而已。

所以要往fail链上调整,直到当前点代表的长度小于当前串的长度。

对于查询,可以近似的看作是查两个点的路径上的所有点的出现次数*长度,当然,这两个点也是需要调整长度后的点。

注意如果说这两个点的lca在串中代表的串再往后一个也是相同的,那么我在变换中必然不会到达lca,所以这种情况要特判。

对于答案的统计可以用树链剖分+线段树维护,对于调整长度的操作可以用树链剖分+二分维护,那么这题的复杂度是 O(n(log n)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();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值