[后缀自动机][LCT] BZOJ 2555: SubString

Solution

出现的次数就是匹配到的点的 rightu 集合大小。
一个点的 |rightu| 有变化会对 parent 树上的祖先造成相应的改变。
因为这题强制在线,所以就用LCT维护 |rightv| 啦。

#include <bits/stdc++.h>
#define show(x) cerr << #x << " = " << x << endl
using namespace std;
typedef long long ll;
typedef pair<int, int> Pairs;

const int N = 1512121;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0; int sgn = 0;
    for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
    if (sgn) x = -x;
}
inline void read(char *ch) {
    char c; int len = 0;
    for (c = get(); c < 'A' || c > 'Z'; c = get());
    for (; c >= 'A' && c <= 'Z'; c = get()) ch[len++] = c;
    ch[len] = 0;
}

namespace LCT {
    struct node {
        node *ch[2];
        node *fa;
        int add, key;
        inline void Mark(int x) {
            add += x; key += x;
        }
        inline void PushDown(void) {
            if (add) {
                ch[0]->Mark(add);
                ch[1]->Mark(add);
                add = 0;
            }
        }
    };
    node T[N];
    node *null;
    inline void Init(int n) {
        null = T;
        null->ch[0] = null->ch[1] = null->fa = null;
        null->key = null->add = 0;
        for (int i = 1; i <= n; i++) {
            T[i].ch[0] = T[i].ch[1] = T[i].fa = null;
            T[i].add = T[i].key = 0;
        }
    }
    inline bool IsRoot(node *x) {
        return x->fa == null || (x->fa->ch[0] != x && x->fa->ch[1] != x);
    }
    inline void Rotate(node *x) {
        node *y = x->fa, *z = y->fa;
        int l = (y->ch[0] != x), r = l ^ 1;
        if (!IsRoot(y)) {
            if (z->ch[0] == y) z->ch[0] = x;
            else z->ch[1] = x;
        }
        x->fa = z; y->fa = x; x->ch[r]->fa = y;
        y->ch[l] = x->ch[r]; x->ch[r] = y;
    }
    inline void Down(node *x) {
        if (!IsRoot(x)) Down(x->fa);
        x->PushDown();
    }
    inline void Splay(node *x) {
        Down(x);
        while (!IsRoot(x)) {
            node *y = x->fa, *z = y->fa;
            if (!IsRoot(y)) {
                if (y->ch[0] == x ^ z->ch[0] == y) Rotate(x);
                else Rotate(y);
            }
            Rotate(x);
        }
    }
    inline void Access(node *x) {
        for (node *y = null; x != null; x = x->fa) {
            Splay(x); x->ch[1] = y; y = x;
        }
    }
    inline void Link(node *x, node *y) {
        Access(y); Splay(y);
        x->fa = y; y->Mark(x->key);
    }
    inline void Cut(node *x) {
        Access(x); Splay(x);
        x->ch[0]->Mark(-x->key);
        x->ch[0]->fa = null; x->ch[0] = null;
    }
    inline void Link(int x, int y) {
        Link(T + x, T + y);
    }
    inline void Cut(int x) {
        Cut(T + x);
    }
    inline int Val(int x) {
        Splay(T + x); return T[x].key;
    }
}
namespace SAM {
    int to[N][27];
    int mx[N], par[N];
    int last, root, Tcnt;
    void Init(void) { last = root = Tcnt = 1; }
    inline void Link(int x, int y) {
        par[x] = y; LCT::Link(x, y);
    }
    inline int Extend(int c) {
        int p = last, np = ++Tcnt;
        mx[np] = mx[p] + 1; LCT::T[np].key = 1;
        for (; p && !to[p][c]; p = par[p]) to[p][c] = np;
        if (p) {
            int q = to[p][c];
            if (mx[q] != mx[p] + 1) {
                int nq = ++Tcnt;
                mx[nq] = mx[p] + 1;
                memcpy(to[nq], to[q], sizeof to[q]);
                Link(nq, par[q]); LCT::Cut(q);
                Link(q, nq); Link(np, nq);
                for (; to[p][c] == q; p = par[p])
                    to[p][c] = nq;
            } else {
                Link(np, q);
            }
        } else {
            Link(np, root);
        }
        return last = np;
    }
    inline int Count(char *begin, char *end) {
        int u = root;
        for (char *c = begin; c != end; c++) {
            int x = *c - 'A';
            if (to[u][x]) u = to[u][x];
            else return 0;
        }
        return LCT::Val(u);
    }
}

int q, n, ans, mask;
char in[N], opt[N], s[N];

inline void Decode(int mask) {
    for (int i = 0; i < n; i++) {
        mask = (mask * 131 + i) % n;
        swap(s[mask], s[i]);
    }
}  

int main(void) {
    freopen("1.in", "r", stdin);
    read(q); read(in); n = strlen(in);
    SAM::Init(); LCT::Init(666666 << 1);
    for (int i = 0; i < n; i++)
        SAM::Extend(in[i] - 'A');
    while (q--) {
        read(opt); read(s);
        n = strlen(s); Decode(mask);
        if (*opt == 'Q') {
            printf("%d\n", ans = SAM::Count(s, s + n));
            mask ^= ans;
        } else {
            for (int i = 0; i < n; i++)
                SAM::Extend(s[i] - 'A');
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值