华南理工大学“三七互娱杯” I.HRY and repeaters(sam+线段树合并)

题面:https://ac.nowcoder.com/acm/contest/874/I

博客明天补,准备打cf,先贴代码。

sam:听说你强制在线?好巧哦我就是在线的。

5.5日:
咕咕咕。
现场不敢开不会开,最后现场0队通过。
回来看了一下牛客当时唯一的ac代码,发现卧槽这是个板子题。
然后大家看到第一想法都是“可持久化后缀自动机”hhhh
贴一句葫芦爷的话吧:“ 只求right集合大小的话,直接拓扑排序(计数排序)然后dp。要求每个点的right集合具体有什么的话,一般线段树合并 或者 dfs序主席树。”

把right集用线段树维护,建广义sam时继续一下有哪些串路过了当前right集,之后拓扑排序,从后往前跑个线段树合并,就可以求出维护了所有串贡献的right集-线段树version。

线段树用动态开点来优化。

ac代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int maxn = 3e5 + 5;
int n, q;
string s, t;

int sum[maxn * 60], ls[maxn * 60], rs[maxn * 60];
int tot;

void update(int &i, int l, int r, int pos) {
    if (!i) {
        i = ++tot;
    }
    ++sum[i];
    if (l == r) {
        return;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid) {
        update(ls[i], l, mid, pos);
    } else {
        update(rs[i], mid + 1, r, pos);
    }
}

int merge(int x, int y) {
    if (!x || !y) {
        return x + y;
    }
    int z = ++tot;
    sum[z] = sum[x] + sum[y];
    ls[z] = merge(ls[x], ls[y]);
    rs[z] = merge(rs[x], rs[y]);
    return z;
}

int query(int i, int l, int r, int L, int R) {
    if ((l >= L && r <= R) || !i) {
        return sum[i];
    }
    int ans = 0, mid = (l + r) >> 1;
    if (L <= mid) {
        ans += query(ls[i], l, mid, L, R);
    }
    if (R > mid) {
        ans += query(rs[i], mid + 1, r, L, R);
    }
    return ans;
}

struct Sam {
    int next[maxn << 1][26];
    int link[maxn << 1], step[maxn << 1];
    int endpos[maxn << 1];
    int a[maxn], b[maxn << 1];
    int sz, last, root;

    void init() {
        //如多次建立自动机,加入memset操作
        root = sz = last = 1;
    }

    void add(int c) {
        if (next[last][c] && step[last] + 1 == step[next[last][c]]) {
            last = next[last][c];
            return;
        }

        int p = last;
        int np = ++sz;
        last = np;

        step[np] = step[p] + 1;
        while (!next[p][c] && p) {
            next[p][c] = np;
            p = link[p];
        }

        if (p == 0) {
            link[np] = root;
        } else {
            int q = next[p][c];
            if (step[p] + 1 == step[q]) {
                link[np] = q;
            } else {
                int nq = ++sz;
                memcpy(next[nq], next[q], sizeof(next[q]));
                step[nq] = step[p] + 1;
                link[nq] = link[q];
                link[q] = link[np] = nq;
                while (next[p][c] == q && p) {
                    next[p][c] = nq;
                    p = link[p];
                }
            }
        }
    }

    void build() {
        init();
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            cin >> s;
            last = root;
            for (int j = 0; j < s.length(); ++j) {
                add(s[j] - 'a');
                //need an update
                update(endpos[last], 1, n, i);
            }
        }
        for (int i = 1; i <= sz; i++) {
            a[step[i]]++;
        }
        for (int i = 1; i <= sz; i++) {
            a[i] += a[i - 1];
        }
        for (int i = 1; i <= sz; i++) {
            b[a[step[i]]--] = i;
        }
        for (int i = sz; i > root; --i) {
            int e = b[i];
             endpos[link[e]] = merge(endpos[link[e]], endpos[e]);
        }
    }

    int solve(int l, int r) {
        int p = root;
        for (int i = 0, c; i < t.length(); ++i) {
            c = t[i] - 'a';
            if (!next[p][c]) {
                return 0;
            }
            p = next[p][c];
        }
        return query(endpos[p], 1, n, l, r);
    }
} sam;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    sam.build();
    cin >> q;
    int l, r, ans = 0;
    while (q--) {
        cin >> l >> r >> t;
        l ^= ans, r ^= ans;
        ans = sam.solve(l, r);
        cout << ans << '\n';
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值