1. 程式人生 > >Bzoj 1014&Luogu 4036 火星人Prefix(FHQ-Treap)

Bzoj 1014&Luogu 4036 火星人Prefix(FHQ-Treap)

題面

洛谷

Bzoj

題解

首先,這種帶修改的是不能用$SA$的,然後,我們做$SA$的題一般也能二分+$Hash$,所以不妨考慮用$FHQ-Treap$維護樹,然後查詢就用二分+$Hash$。

$Hash$怎麼寫?

$ hash[o]=hash[lc[o]]\times base[siz[rc[o]]+1]+val[o]\times base[siz[rc[o]]]+hash[rc[o]] $

為什麼可以這麼寫呢?想一想,為什麼

我們一般怎麼求一顆維護序列的平衡樹的原序列呢?—中序遍歷

所以嘛,一棵子樹的雜湊值可以轉化成它左子樹的雜湊值+本身的值+右子樹雜湊值

由於怕重複,所以可以考慮將前面兩個值隨便乘上一點什麼東西(比如左子樹或者右子樹的$size$之類的)

#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

template<typename T>
void read(T &x) {
    int flag = 1; x = 0; char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch == '-') flag = -flag; ch = getchar(); }
    while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); x *= flag;
}

const int N = 3e5 + 10;
char s[N];
int m, len, base[N];
int rt, tot, lc[N], rc[N], pri[N], siz[N], h[N], val[N];

inline void upt(int o) {
    siz[o] = siz[lc[o]] + siz[rc[o]] + 1;
    h[o] = h[lc[o]] * base[siz[rc[o]] + 1] + val[o] * base[siz[rc[o]]] + h[rc[o]];
}
inline int node(int x) { siz[++tot] = 1, val[tot] = h[tot] = x, pri[tot] = rand(); return tot; }
void split(int o, int k, int &l, int &r) {
    if(!o) { l = r = 0; return ; }
    if(siz[lc[o]] < k) l = o, split(rc[o], k - siz[lc[o]] - 1, rc[o], r);
    else r = o, split(lc[o], k, l, lc[o]);
    upt(o);
}
int merge(int l, int r) {
    if(!l || !r) return l + r;
    if(pri[l] < pri[r]) { rc[l] = merge(rc[l], r), upt(l); return l; }
    else { lc[r] = merge(l, lc[r]), upt(r); return r; }
}
inline int query(int l, int r) {
    int x, y, k, ret;
    split(rt, r, x, y), split(x, l - 1, x, k);
    ret = h[k], rt = merge(merge(x, k), y);
    return ret;
}

int main () {
    scanf("%s", s + 1), len = strlen(s + 1), srand(19260817), base[0] = 1;
    for(int i = 1; i < N; ++i) base[i] = base[i - 1] * 27;
    for(int i = 1; i <= len; ++i) rt = merge(rt, node(s[i] - 'a' + 1));
    read(m); char opt[5], ch[5]; int x, y, l, r, k;
    while(m--) {
        scanf("%s", opt), read(x);
        if(opt[0] == 'Q') {
            read(y); int L = 0, R = std::min(len - x, len - y) + 1, ret;
            while(L <= R) {
                int mid = (L + R) >> 1;
                if(query(x, x + mid - 1) == query(y, y + mid - 1)) ret = mid, L = mid + 1;
                else R = mid - 1;
            } printf("%d\n", ret);
        } else {
            scanf("%s", ch), split(rt, x, l, r), ++len;
            if(opt[0] == 'R') --len, split(l, x - 1, l, k);
            rt = merge(merge(l, node(ch[0] - 'a' + 1)), r);
        }
    }
    return 0;
}