[CF 666E] Forensic Examination

Description

传送门

Solution

Code

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>

const int N = 500005, M = 9500005;
struct Edge {
    int v, nxt;
} e[N];
struct Node {
    int l, r, x, y, id;
    bool operator < (const Node & rhs) const {
        return y < rhs.y;
    }
} a[N];
struct Pair {
    int x, y;
    bool operator < (const Pair & rhs) const {
        return (x == rhs.x && y > rhs.y) || x < rhs.x;
    }
} sum[M], ans[N];
int L[N], q, n, m, mx[N], ch[N][30], sz = 1, las = 1, head[N], tot, fa[N], cnt, ls[M], rs[M], f[20][N], rt[N];
char s[N], t[N];
std::vector<int> b[N];

int read() {
    int x = 0; char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    return x;
}
void adde(int u, int v) {
    e[++tot].nxt = head[u], head[u] = tot, e[tot].v = v;
}
void ins(int c) {
    int p = las, np = ++sz;
    mx[np] = mx[p] + 1, las = np;
    for (; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
    if (!p) { fa[np] = 1; return; }
    int q = ch[p][c];
    if (mx[p] + 1 == mx[q]) { fa[np] = q; return; }
    int nq = ++sz;
    mx[nq] = mx[p] + 1, fa[nq] = fa[q], fa[np] = fa[q] = nq;
    memcpy(ch[nq], ch[q], sizeof ch[q]);
    for (; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
}
void update(int &x, int l, int r, int p) {
    if (!x) x = ++cnt;
    if (l == r) { sum[x].x = 1, sum[x].y = l; return; }
    int mid = (l + r) >> 1;
    if (p <= mid) update(ls[x], l, mid, p);
    else update(rs[x], mid + 1, r, p);
    sum[x] = std::max(sum[ls[x]], sum[rs[x]]);
}
Pair query(int x, int l, int r, int L, int R) {
    Pair res; res.x = 0, res.y = 0;
    if (!x) return res;
    if (L <= l && r <= R) return sum[x];
    int mid = (l + r) >> 1;
    if (L <= mid) res = query(ls[x], l, mid, L, R);
    if (mid < R) res = std::max(res, query(rs[x], mid + 1, r, L, R));
    return res;
}
int merge(int x, int y) {
    if (!x) return y;
    if (!y) return x;
    if (!ls[x] && !rs[x] && !ls[y] && !rs[y]) { sum[x].x += sum[y].x; return x; }
    ls[x] = merge(ls[x], ls[y]);
    rs[x] = merge(rs[x], rs[y]);
    sum[x] = std::max(sum[ls[x]], sum[rs[x]]);
    return x;
}
void dfs1(int u) {
    f[0][u] = fa[u];
    for (int i = 1; i <= 18; ++i) f[i][u] = f[i - 1][f[i - 1][u]];
    for (int i = head[u]; i; i = e[i].nxt) dfs1(e[i].v);
}
void jump(int x, int k) {
    for (int i = 18; ~i; --i)
        if (mx[f[i][x]] >= a[k].y - a[k].x + 1) x = f[i][x];
    b[x].push_back(k);
}
void dfs2() {
    int x = 1, y = 0;
    for (int i = 1, j = 1; i <= n; ++i) {
        int c = s[i - 1] - 'a';
        while (x && !ch[x][c]) x = fa[x], y = mx[x];
        if (!x) { x = 1, y = 0; continue; }
        ++y, x = ch[x][c];
        while (j <= q && a[j].y < i) ++j;
        while (j <= q && a[j].y == i) {
            if (y >= a[j].y - a[j].x + 1) jump(x, j);
            ++j;
        }
    }
}
void dfs3(int u) {
    for (int i = head[u]; i; i = e[i].nxt)
        dfs3(e[i].v), rt[u] = merge(rt[u], rt[e[i].v]);
    int sz = b[u].size();
    for (int i = 0; i < sz; ++i) ans[a[b[u][i]].id] = query(rt[u], 1, m, a[b[u][i]].l, a[b[u][i]].r);
}
int main() {
    scanf("%s", s), m = read();
    for (int i = 1; i <= m; ++i) {
        scanf("%s", t), n = strlen(t), las = 1;
        for (int j = 0; j < n; ++j)
            ins(t[j] - 'a'), update(rt[las], 1, m, i);
    }
    q = read(), n = strlen(s);
    for (int i = 1; i <= q; ++i) L[i] = a[i].l = read(), a[i].r = read(), a[i].x = read(), a[i].y = read(), a[i].id = i;
    std::sort(a + 1, a + q + 1);
    for (int i = 2; i <= sz; ++i) adde(fa[i], i);
    dfs1(1), dfs2(), dfs3(1);
    for (int i = 1; i <= q; ++i) printf("%d %d\n", ans[i].y ? ans[i].y : L[i], ans[i].x);
    return 0;
}

原文地址:https://www.cnblogs.com/fly-in-milkyway/p/10684956.html

时间: 2024-08-01 02:43:42

[CF 666E] Forensic Examination的相关文章

CF.666E.Forensic Examination(广义后缀自动机 线段树合并)

题目链接 \(Description\) 给定串S和m个串Ti.Q次询问,每次询问l,r,pl,pr,求S[pl~pr]在Tl~Tr中的哪个串出现次数最多,输出最多次数及其T的下标.若有多个,输出下标最小的. \(Solution\) 挺好的题吧 对T个串建SAM,然后要求出SAM每个节点上|right|最大的是哪个串. 每个节点的|right|可以在DFS parent树时合并子节点得到,如果用线段树维护,|right|最大的位置也可以合并得到. 这样可以离线处理询问,最后DFS一遍得到答案.

Codeforces 666E Forensic Examination SAM+权值线段树

第一次做这种$SAM$带权值线段树合并的题 然而$zjq$神犇看完题一顿狂码就做出来了 $Orz$ 首先把所有串当成一个串建$SAM$ 我们对$SAM$上每个点 建一棵权值线段树 每个叶子节点表示一个匹配串能到达这个点的子串个数 这样我们对最后的$SAM$的权值线段树按$parent$树合并 询问的时候找到对应的$SAM$上的权值线段树直接查询就好了 具体的操作看代码吧~ #include<bits/stdc++.h> using namespace std; #define FO(x) {f

【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并

题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r$ 中的哪一个里出现次数最多,输出出现次数最多的串编号(如果有多个则输出编号最小的)以及相应出现次数. $|S|,q\le 5\times 10^5$ ,$\sum\limits_{i=1}^m|T_i|\le 5\times 10^4$ . 题解 广义后缀自动机+树上倍增+线段树合并 对 $S$

[CF666E]Forensic Examination

luogu 题意 给你一个串\(S\)以及一个字符串数组\(T[1..m]\),\(q\)次询问,每次问\(S\)的子串\(S[p_l..p_r]\)在\(T[l..r]\)中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. \(|S|\le5*10^5,\sum|T|\le5*10^4,q\le5*10^5\) sol 首先肯定是对\(T[1..m]\)这个字符串数组构出广义\(SAM\). 考虑串\(S\)在这个\(SAM\)上的匹配,是\(O(|S|)\)的.显然不

CF666E Forensic Examination(后缀自动机+线段树合并)

给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[pl..pr]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 我们首先对m个字符串数组建出后缀自动机,然后我们可以通过跳trans边找到S前i个字符代表的前缀的最长后缀.我们要找的是S[pl..pr]并不是以pr结束最长的后缀,但我们可以确定S[pl..pr]一定是当前点的祖先所以当我们跳到pr代表的点时我们倍增往上跳知道找到一个点的长度刚好大于等于pr-pl+1,这个点就是询问

CF666E 【Forensic Examination】

题目 每天一道\(SAM\)真是非常开心 一看就是广义\(SAM\)+线段树合并了 我们存好\(S\)串每一个前缀的终点,之后在\(parent\)树上倍增找到表示\(S[l,r]\)这个子串的节点,我们用线段树合并维护好\(endpos\)集合,查一个区间最大值就好了 代码 #include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #define maxn 20000

CF666E Forensic Examination SAM+倍增,线段树和并

题面: 给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[p_l..p_r]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数.如有多解输出最靠前的那一个. 分析: 第一次见这道题时,由于对算法十分陌生,就将其压进了任务计划,这一天又提到了这道题,这才算是重见天日. 数据结构题,越来越注重多种数据结构的搭配使用.搭配使用呢,我们就要知道各种数据结构的功能与适用范围,下面是这道题一个理想的思考过程(虽然我是抄的题解--) 首先,一个模式串的区间要在多个串上进行匹配,

CF666E Forensic Examination(广义后缀自动机+线段树合并)

Luogu 给你一个串 $ S $ 以及一个字符串数组 $ T_1 ~ T_m $ , $ q $ 次询问,每次问 $ S $ 的子串S[p_l,p_r]在 $ T_l ~ T_r $ 中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 题解时间 SAM的毒瘤题,无论是倍增来满足长度限制,线段树合并来求区间询问,应有尽有... 对于 $ T $ 串建广义SAM,之后考虑如何使得 $ S $ 在SAM上匹配时求出 $ S $ 在每个 $ T $ 的出现次数. 很明显用线段树

CF666E Forensic Examination [后缀自动机,线段树合并]

题意: 给出一个串 \(S\),再给出 \(n\) 个串 \(T_i\), \(q\) 次询问 \(S[pl,pr]\) 在 $ T_{[l,r]}$哪个串出现次数最多. solution: 不难想到我们找 \(S[pl,pr]\) 是可以记录 \(ed_{pr}\) 然后倍增上去找到这个区间所对应的 SAM 节点. 我们把 \(T_i\) 插入 SAM 里,并且对应节点搞上 \(i\),然后合并就好了qwq. SAM 某个子树部分都是包含他自己的串,所以线段树合并一下就变成了子树数颜色以及找到