首页 > 其他分享 >【题解】CF1073G Yet Another LCP Problem

【题解】CF1073G Yet Another LCP Problem

时间:2023-01-05 09:12:53浏览次数:67  
标签:const LCP SAM int 题解 son CF1073G

题面很清楚,不概括了。

思路

后缀树 + 树剖。

套路题。

看到 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;
}

标签:const,LCP,SAM,int,题解,son,CF1073G
From: https://www.cnblogs.com/lingspace/p/cf1073g.html

相关文章

  • 【题解】CF808E Selling Souvenirs
    题意多重背包,但数据范围很大并且体积小于等于三。思路乱搞。很自然地考虑将物品按照体积分成三类。显然对于同一类的物品从最大开始取最优,那么有一个贪心的想法。直......
  • 【题解】P5305 [GXOI/GZOI2019]旧词
    题面很清楚,不概括题意了。思路树剖。\(k=1\)的情况是P4211[LNOI2014]LCA具体来说,只需要\(\forall1\leqi\leqx\),将\(1\)到\(i\)的路径上每一个结点权值......
  • 题解 校内考20230104 T2 旗木双翼
    题目描述菲菲和牛牛在一块\(n\)行\(m\)列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。落子......
  • LOJ 数列分块入门 9 题解题报告
    LOJ数列分块入门9题解题报告\(\text{ByDaiRuiChen007}\)I.数列分块入门1题目大意\(\text{Link}\)维护一个长度为\(n\)的序列,支持区间加,单点查值思路分析简......
  • IntelliJ IDEA常见问题解决办法汇总
     mac上idea升级到2020.2.2后,发现versioncontrol中的localchanges不见了!解决办法:View—>ToolWIndows—>Commit【点击下,就会提示要把这个Commit放在IDEA面板那个位置,选择......
  • git 不区分大小写问题解决
    使用vscode   更改vue文件为大驼峰的方式 发现本地代码和提交时的代码名称不同是因为git不区分大小写这时我们需要找到代码存放位置 鼠标右键  GitBashHer......
  • 异地多活回环同步问题解决方案
    1.异地多活:一般为两地三中心或者三地五中心,这样设计是为了在发生单点故障或网络分区时,集群能继续提供服务。两地三中心可以容忍机房级别灾难,三地五中心可以容忍城市级别灾......
  • Codeforces Hello 2023 CF 1779 A~F 题解
    点我看题A.HallofFame先把不用操作就合法的情况判掉。然后发现交换LL,RR,RL都是没用的,只有交换LR是有用的,这样可以照亮相邻的两个位置。所以我们就是要找到一个位置i,......
  • Codeforces Hello 2023 CF 1779 A~F 题解
    点我看题A.HallofFame先把不用操作就合法的情况判掉。然后发现交换LL,RR,RL都是没用的,只有交换LR是有用的,这样可以照亮相邻的两个位置。所以我们就是要找到一个位置i,......
  • 题解 P3526 [POI2011]OKR-Periodicity
    P3526[POI2011]OKR-Periodicitynb题。先说一下这题的入手点。不妨假设一个字符串一定存在一个短周期(约定周期\(p\)满足\(2p\leq|s|\)的为短周期),假设短周期的长度......