Loading

【题解】CF1073G Yet Another LCP Problem

题面很清楚,不概括了。

思路

后缀树 + 树剖。

套路题。

看到 lcp 考虑转化成后缀树上求 lca,这里可以用 SAM 构造反串的 parent tree 解撅(f**k u ukk)

于是问题转化成:

每次给定两个集合 \(A, B\),求 \(\sum\limits_{i \in A} \sum\limits_{j \in B} \operatorname{depth}(\operatorname{lca}(i, j))\)

这个和 【题解】P5305 [GXOI/GZOI2019]旧词 是一个套路,考虑树剖就行。

代码

#include <cstdio>
#include <cstring>
using namespace std;

typedef long long ll;

const int maxn = 2e5 + 5;
const int maxl = 2e5 + 5;
const int maxe = maxn << 1;
const int sam_sz = maxn << 1;

struct node
{
    int to, nxt;
} edge[maxe];

int n, m, q, cnt;
int idx[maxn];
int a[maxl], b[maxl];
int head[sam_sz], fa[sam_sz], son[sam_sz], top[sam_sz];
int dep[sam_sz], dfn[sam_sz], rk[sam_sz], sz[sam_sz], w[sam_sz];
char str[maxn];

namespace SAM
{
    int lst = 1, cur = 1;
    int len[sam_sz], fa[sam_sz], son[sam_sz][26];

    void copy_node(int to, int from)
    {
        len[to] = len[from], fa[to] = fa[from];
        memcpy(son[to], son[from], sizeof(son[to]));
    }

    void insert(int c)
    {
        int p = lst, np = lst = ++cur;
        len[np] = len[p] + 1;
        for ( ; p && (!son[p][c]); p = fa[p]) son[p][c] = np;
        if (!p) fa[np] = 1;
        else
        {
            int q = son[p][c];
            if (len[q] == len[p] + 1) fa[np] = q;
            else
            {
                int nq = ++cur;
                copy_node(nq, q);
                len[nq] = len[p] + 1;
                fa[q] = fa[np] = nq;
                for ( ; p && (son[p][c] == q); p = fa[p]) son[p][c] = nq;
            }
        }
    }
}

namespace SGT
{
    struct node
    {
        int l, r, mul, lazy;
        ll sum;
    } tree[sam_sz << 2];

    void push_up(int k) { tree[k].sum = tree[k << 1].sum + tree[k << 1 | 1].sum; }

    void push_down(int k)
    {
        if (!tree[k].lazy) return;
        tree[k << 1].sum += 1ll * tree[k << 1].mul * tree[k].lazy;
        tree[k << 1 | 1].sum += 1ll * tree[k << 1 | 1].mul * tree[k].lazy;
        tree[k << 1].lazy += tree[k].lazy, tree[k << 1 | 1].lazy += tree[k].lazy;
        tree[k].lazy = 0;
    }

    void build(int k, int l, int r)
    {
        tree[k].l = l, tree[k].r = r;
        if (l == r)
        {
            tree[k].mul = w[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(k << 1, l, mid);
        build(k << 1 | 1, mid + 1, r);
        tree[k].mul = tree[k << 1].mul + tree[k << 1 | 1].mul;
    }

    void update(int k, int l, int r, int w)
    {
        if ((tree[k].l >= l) && (tree[k].r <= r))
        {
            tree[k].sum += 1ll * tree[k].mul * w;
            tree[k].lazy += w;
            return;
        }
        push_down(k);
        int mid = (tree[k].l + tree[k].r) >> 1;
        if (l <= mid) update(k << 1, l, r, w);
        if (r > mid) update(k << 1 | 1, l, r, w);
        push_up(k);
    }

    ll query(int k, int l, int r)
    {
        if ((tree[k].l >= l) && (tree[k].r <= r)) return tree[k].sum;
        push_down(k);
        int mid = (tree[k].l + tree[k].r) >> 1;
        ll res = 0;
        if (l <= mid) res += query(k << 1, l, r);
        if (r > mid) res += query(k << 1 | 1, l, r);
        return res;
    }

    void updatep(int u, int w)
    {
        while (u)
        {
            update(1, dfn[top[u]], dfn[u], w);
            u = fa[top[u]];
        }
    }

    ll queryp(int u)
    {
        ll res = 0;
        while (u)
        {
            res += query(1, dfn[top[u]], dfn[u]);
            u = fa[top[u]];
        }
        return res;
    }
}

void add_edge(int u, int v)
{
    cnt++;
    edge[cnt].to = v;
    edge[cnt].nxt = head[u];
    head[u] = cnt;
}

void dfs1(int u, int f)
{
    fa[u] = f;
    dep[u] = dep[f] + 1;
    sz[u] = 1;
    for (int i = head[u]; i; i = edge[i].nxt)
    {
        int v = edge[i].to;
        dfs1(v, u);
        sz[u] += sz[v];
        if (sz[v] > sz[son[u]]) son[u] = v;
    }
}

void dfs2(int u, int t)
{
    top[u] = t;
    dfn[u] = ++cnt;
    rk[cnt] = u;
    w[cnt] = SAM::len[u] - SAM::len[SAM::fa[u]];
    if (son[u]) dfs2(son[u], t);
    for (int i = head[u]; i; i = edge[i].nxt)
    {
        int v = edge[i].to;
        if (v != son[u]) dfs2(v, v);
    }
}

int main()
{
    // freopen("CF1073G_0.in", "r", stdin);
    // freopen("CF1073G_0.out", "w", stdout);
    scanf("%d%d%s", &n, &q, str + 1);
    for (int i = n; i; i--) SAM::insert(str[i] - 'a'), idx[i] = SAM::lst;
    m = SAM::cur;
    for (int i = 2; i <= m; i++) add_edge(SAM::fa[i], i);
    cnt = 0;
    dfs1(1, 0);
    dfs2(1, 1);
    SGT::build(1, 1, m);
    while (q--)
    {
        int ki, li;
        ll ans = 0;
        scanf("%d%d", &ki, &li);
        for (int i = 1; i <= ki; i++)
        {
            scanf("%d", &a[i]);
            SGT::updatep(idx[a[i]], 1);
        }
        for (int i = 1; i <= li; i++)
        {
            scanf("%d", &b[i]);
            ans += SGT::queryp(idx[b[i]]);
        }
        // for (int i = 1; i <= ki; i++) ans += SGT::queryp(idx[a[i]]);
        for (int i = 1; i <= ki; i++) SGT::updatep(idx[a[i]], -1);
        printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2023-01-05 08:56  kymru  阅读(21)  评论(0编辑  收藏  举报