题意

给你 \(n\) 个串 \(s_{1\cdots n}\) ,每次询问给出 \(l,r,k\) ,问在 \(s_{l\cdots r}\) 中出现了多少次 \(s_k\) 。

\(n,q,\sum|s|\le 10^5\)

分析

  • 先建AC自动机的 \(fail\) 树, 我们考虑两种暴力:

    • 将 \(l​\) 到 \(r​\) 中的每个串的末尾节点子树标记,查询 \(s_k​\) 的所有节点 \(fail​\) 树到根的路径和。
    • 将 \(s_k\) 的每个节点的子树标记,查询 \(l\) 到 \(r\) 中的每个末尾节点的点权和。
  • 发现这两种做法在不同的数据下有着不同的效果,考虑根号分治:

    • 如果 \(|s_k|\le\sqrt n\) 采用第一种方式,差分查询,这样操作每个串的次数不超过 \(\sqrt n\) ,动态维护前缀和。
    • 如果 \(|s_k|>\sqrt n\) 采用第二种方式,记录前缀和即可,这样的串不超过 \(\sqrt n\) 个。
  • 我们发现,对于 \(dfs\) 序数组来说,修改次数是 \(O(n)\) 级别,但是查询次数却是 \(O(n\sqrt n)\) 级别的,能不能平衡两种操作时间复杂度呢?

    考虑分块来维护前缀和,每个块维护一个加标记。这样修改变成了 \(O(\sqrt n)\) ,但是查询变成了 \(O(1)\) 。

  • 总时间复杂度为 \(O(n\sqrt n)\)。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define go(u) for(int i = head[u], v = e[i].to; i; i=e[i].lst, v=e[i].to)
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define pb push_back
#define re(x) memset(x, 0, sizeof x)
inline int gi() {
int x = 0,f = 1;
char ch = getchar();
while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) { x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
return x * f;
}
template <typename T> inline void Max(T &a, T b){if(a < b) a = b;}
template <typename T> inline void Min(T &a, T b){if(a > b) a = b;}
const int N = 1e5 + 7;
int n, q, sz = 317, tim;
int L[N], R[N], in[N], out[N], ch[N][26];
char s[N];
namespace tr{
int edc;
int head[N];
struct edge {
int lst, to;
edge(){}edge(int lst, int to):lst(lst), to(to) {}
}e[N];
void Add(int a, int b) {
e[++edc] = edge(head[a], b), head[a] = edc;
}
void dfs(int u) {
in[u] = ++tim; go(u) dfs(v); out[u] = tim;
}
}
namespace ac {
int endp[N], fail[N], ndc;
int idx(char c) { return c - 'a';}
void ins(int a) {
L[a] = R[a - 1] + 1;
scanf("%s", s + L[a]);
R[a] = L[a] + strlen(s + L[a]) - 1;
int u = 0;
for(int i = L[a]; i <= R[a]; ++i) {
int c = idx(s[i]);
if(!ch[u][c]) ch[u][c] = ++ndc;
u = ch[u][c];
}
endp[a] = u;
}
void getfail() {
queue<int>Q;
for(int c = 0; c < 26; ++c) if(ch[0][c]) Q.push(ch[0][c]), tr::Add(0, ch[0][c]);
while(!Q.empty()) {
int u = Q.front();Q.pop();
for(int c = 0; c < 26; ++c) {
int &v = ch[u][c];
if(!v) { v = ch[fail[u]][c]; continue;}
fail[v] = ch[fail[u]][c];
Q.push(v);
tr::Add(fail[v], v);
}
}
}
}
struct data {
int l, r, k, id, opt;
bool operator <(const data &rhs) const {
return r < rhs.r;
}
}t[N << 1];
vector<data>G[N];
int qc, bl[N];//时间戳数组长度为tim
int Rp(int x){ return min(tim, x * sz);}
LL ans[N], sum[N], pre[N], add[400];
void mdf(int p, int v) {
if(p == tim + 1) return;
for(int i = p; i <= Rp(bl[p]); ++i) pre[i] +=v;
for(int i = bl[p] + 1; i <= bl[tim]; ++i) add[i] += v;
}
LL qry(int p) { return pre[p] + add[bl[p]]; }
void modify(int l, int r) { mdf(l, 1); mdf(r + 1, -1);}
LL query(int l, int r) { return qry(r) - qry(l - 1); }
void solve(int x) {
re(sum), re(pre), re(add);
int u = 0;
for(int i = L[x]; i <= R[x]; ++i) {
u = ch[u][ac::idx(s[i])];
mdf(in[u], 1);
}
for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + query(in[ac::endp[i]], out[ac::endp[i]]);
for(auto v: G[x]) {
ans[v.id] = sum[v.r] - sum[v.l - 1];
}
}
void Addstring(int x) {
int u = ac::endp[x];
modify(in[u], out[u]);
}
LL Substring(int x) {
int u = 0;LL ans = 0;
for(int i = L[x]; i <= R[x]; ++i) {
u = ch[u][ac::idx(s[i])];
ans += qry(in[u]);
}
return ans;
}
int main() {
n = gi(), q = gi();
rep(i, 1, n) ac::ins(i);
ac::getfail(), tr::dfs(0);
rep(i, 1, tim) bl[i] = (i - 1) / sz + 1; rep(i, 1, q) {
int l = gi(), r = gi(), k = gi();
if(R[k] - L[k] + 1 <= sz) {
t[++qc] = (data){ 0, l - 1, k, i, -1 };
t[++qc] = (data){ 0, r, k, i, 1 };
}else
G[k].pb((data){ l, r, k, i, 1});
}
rep(i, 1, n) if(R[i] - L[i] + 1 > sz) solve(i); re(pre), re(add);
sort(t + 1, t + 1 + qc);
for(int i = 0, j = 1; i <= n; ++i) {
if(i) Addstring(i);
for(; j <= qc && t[j].r == i; ++j) {
ans[t[j].id] += 1ll * t[j].opt * Substring(t[j].k);
}
}
rep(i, 1, q) printf("%lld\n", ans[i]);
return 0;
}

[CF587F]Duff is Mad[AC自动机+根号分治+分块]的更多相关文章

  1. 【CF587F】Duff is Mad AC自动机+分块

    [CF587F]Duff is Mad 题意:给出n个串$s_1,s_2..s_n$,有q组询问,每次给出l,r,k,问你编号在[l,r]中的所有串在$s_k$中出现了多少次. $\sum|s_i|, ...

  2. CF587F-Duff is Mad【AC自动机,根号分治】

    正题 题目链接:https://www.luogu.com.cn/problem/CF587F 题目大意 给出\(n\)个字符串\(s\).\(q\)次询问给出\(l,r,k\)要求输出\(s_{l. ...

  3. BZOJ.4320.[ShangHai2006]Homework(根号分治 分块)

    BZOJ \(\mathbb{mod}\)一个数\(y\)的最小值,可以考虑枚举剩余系,也就是枚举区间\([0,y),[y,2y),[2y,3y)...\)中的最小值(求后缀最小值也一样)更新答案,复 ...

  4. CF587F Duff is Mad(AC自动机+树状数组+分块)

    考虑两一个暴力 1 因为询问\([a,b]\)可以拆成\([1,b]\)-\([1,a-1]\)所以把询问离线,然后就是求\([1,x]\)中被\(S_i\)包含的串的数量.考虑当\([1,x-1]- ...

  5. CF587F Duff is Mad

    题目 有趣的思想 首先暴力的话,自然是对每一个询问在\(AC\)自动机上跑一遍\(k\),看看跑出来的节点在\(fail\)树到根的路径上有多少个\(l\)到\(r\)之间的结束标记就好了 我们发现无 ...

  6. [CF1083F]The Fair Nut and Amusing Xor[差分+同余分类+根号分治+分块]

    题意 给定两个长度为 \(n\) 的序列 \(\{a_i\}\) 与 \(\{b_i\}\),你需要求出它们的相似度.,我们定义这两个序列的相似度为将其中一个序列转化为另一个序列所需的最小操作次数.一 ...

  7. HDU4787 GRE Words Revenge【AC自动机 分块】

    HDU4787 GRE Words Revenge 题意: \(N\)次操作,每次记录一个\(01\)串或者查询一个\(01\)串能匹配多少个记录的串,强制在线 题解: 在线的AC自动机,利用分块来降 ...

  8. Codeforces 587F - Duff is Mad(根号分治+AC 自动机+树状数组)

    题面传送门 第一眼看成了 CF547E-- 话说 CF587F 和 CF547E 出题人一样欸--还有另一道 AC 自动机的题 CF696D 也是这位名叫 PrinceOfPersia 的出题人出的- ...

  9. NOI.AC#2266-Bacteria【根号分治,倍增】

    正题 题目链接:http://noi.ac/problem/2266 题目大意 给出\(n\)个点的一棵树,有一些边上有中转站(边长度为\(2\),中间有一个中转站),否则就是边长为\(1\). \( ...

随机推荐

  1. python 多进程、多线程

    1.多线程: 下面讲一个简单用法,这个模块比较简单,但是实际使用中会遇到很多坑 from multiprocessing import process def go(s): print "主 ...

  2. [钉钉通知系列]SVN提交后自动推送消息到钉钉群

    钉钉设置机器人配置 1.进入配置机器人入口 2.添加机器人 3.测试WebHook请求 本人使用Postman进行测试 4.配置SVN 4.1 配置 Pre-commit hook 设置提交内容必须包 ...

  3. Python学习--Selenium模块

    1. Python学习--Selenium模块介绍(1) 2.Python学习--Selenium模块学习(2) 其他: 1. Python学习--打码平台

  4. Git Flow 工作模型与使用

    一. Git Flow 工作模型的原理 无规矩不成方圆,但是规矩太多了,则感觉到束缚.我们一个人工作的时候喜欢无拘无束,想怎么干就怎么干,没有人评判,没有人检验.时间久了就会盲目自大,以为增删改查熟悉 ...

  5. 邮件客户端修改密码—OWA

    邮件客户端修改密码—OWA 1.登录OWA 2.输入用户名 3.点击选项 4.更改密码

  6. It was not possible to find any compatible framework version

    It was not possible to find any compatible framework version The specified framework 'Microsoft.NETC ...

  7. October 27th, 2017 Week 43rd Friday

    The only thing predictable about life is its unpredictability. 人生唯一可以预知的,就是它的变化莫测. Is it really unpr ...

  8. 面向对象程序设计_Task7_Summary

    Summary of the ... 题目链接:第七次作业 终于还是迎来了这学期的最后一次作业,唠叨话还是放最后说,先说说计算器这玩意儿吧 贯穿了整个学期的计算器,要是让我对自己做个评价,顶多只是还好 ...

  9. 关于Javascript的des加密

    参考文章:https://www.cnblogs.com/MSMXQ/p/4484348.html 需要先下载CryptoJS文件,然后引入其中的两个文件,可以在github中找到. 直接上代码 &l ...

  10. Oracle 11g AWR 系列五:如何生成 AWR 报告?

    1.生成单实例 AWR 报告: @$ORACLE_HOME/rdbms/admin/awrrpt.sql 2.生成 Oracle RAC AWR 报告: @$ORACLE_HOME/rdbms/adm ...