题意

给你 \(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 };}elseG[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;
}

转载于:https://www.cnblogs.com/yqgAKIOI/p/10171960.html

[CF587F]Duff is Mad[AC自动机+根号分治+分块]相关推荐

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

    正题 题目链接:https://www.luogu.com.cn/problem/CF587F 题目大意 给出nnn个字符串sss.qqq次询问给出l,r,kl,r,kl,r,k要求输出sl..rs_ ...

  2. CF587F Duff is Mad

    题目描述 题解: 有个$O(\sum 询问k串长)$的做法就不说了. 当然是过不去的. 貌似第22个点总长1e5只有三个串 所以考虑对询问的$k$串串长分开算. 先建$fail$树. 对于$s_k&g ...

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

    正题 题目链接:http://noi.ac/problem/2266 题目大意 给出nnn个点的一棵树,有一些边上有中转站(边长度为222,中间有一个中转站),否则就是边长为111. mmm次询问一个 ...

  4. [XSY3320] string (AC自动机,哈希,点分治)

    XSY3320 前置芝士:回文前缀&&borderborderborder 推荐博客 推荐博客 考虑点分治,问题变成求经过重心的回文路径个数. 一条经过重心的回文路径长这样: xxx到 ...

  5. [CTSC2010]珠宝商(点分治+根号分治+后缀自动机)

    [CTSC2010]珠宝商 洛谷题目传送门 简要题意 给定一颗nnn个节点的树,和一个长度为mmm的模式串SSS 树上每个节点都有一个字符 求树上所有路径的点的字符拼成的字符串在SSS中的出现次数之和 ...

  6. 【LOJ6681】yww 与树上的回文串(点分治)(AC自动机)(字符串哈希)(回文串broder理论)

    传送门 社论(题解): 首先长剖重剖都考虑过了,并没有办法支持快速合并,边分更不用说了,权值在边上怎么边分怎么蛋疼. 考虑点分,我们知道如果一个回文串过了重心,他要么就是重心延伸出去的回文前缀,要么它 ...

  7. 【CTSC2010】珠宝商【后缀自动机】【点分治】【根号分治】

    题意:给一棵 nnn 个点的树,每个点有个字符,另给一个长度为 mmm 的特征串,求树上 n2n^2n2 条有向路径在特征串中出现的次数之和. n,m≤5×104n,m\leq 5\times 10^ ...

  8. 树链剖分 or 根号分治 + dfs序 + 树状数组 ---- CF1254 D. Tree Queries

    题目链接 题目大意: 问题转化: 很容易发现:假设修改的节点是vvv. 1.vvv的子树sonvson_vsonv​直接加上(n−size[sonv])n×d\frac{(n-size[son_v]) ...

  9. Codeforces Round #507 (Div. 1) D. You Are Given a Tree 根号分治 + dp

    传送门 题意: 有一颗nnn个节点的树,其中一个简单路径集合被称为kkk合法当且仅当: 树的每个节点至多属于一条路径,且每条路径恰好包含kkk个点. 对于k∈[1,n]k\in [1,n]k∈[1,n ...

最新文章

  1. 如何解决内网中网络被限制的问题
  2. jdk 1结尾和2结尾_“与巢一起工作”的结尾对您意味着什么
  3. 微信公众号网页开发:播放视频,在列表中滑动会脱离文档流
  4. tcpdump 一个比较详细的用法
  5. Activity的传递数据与实例
  6. Jfinal中使用Ueditor遇到的问题【解决办法】
  7. STL -- string类字符串
  8. VMware15pro安装Windows7虚拟机详细教程
  9. 湖南麒麟实时操作系统调优指南
  10. 苏州大学计算机复试python_苏州大学计算机考研复试经验总结
  11. 微信小程序圣诞帽_完成圣诞快乐Web应用程序界面
  12. win101909要不要更新_近年最稳的Win10更新?Win101909值得升级吗
  13. 信息安全 数据赛 铁人三项_2018.5.18信息安全铁人三项赛数据赛题解
  14. KUKA youbot机械臂与Moveit工具包(2)
  15. ftp服务器怎么修改打开方式,ftp服务器怎么修改打开方式
  16. python练习, 打鱼晒网问题
  17. 计算机主机箱工作电流,计算机主板上所用到的电压标准
  18. 词法分析——词法分析的任务
  19. redis关闭后在连接报错误:Could not connect to Redis at 127.0.0.1:6379: Connection refused
  20. 微型计算机课设电梯控制8255,东南大学吴健雄学微机课程设计电梯控制器.doc

热门文章

  1. 5分钟内搞定 Tomcat 的 SSL 配置
  2. 在Cloudshare上使用PAL=〉系统检查及初始化
  3. 《XML程序设计》要点
  4. python写appium测试用例找控件的方法总结
  5. 如何快速安全的插入千万条数据?
  6. 无责任书评:每个Java程序员都应该深入理解Java虚拟机!
  7. 下一代微服务!ServiceMesh的2018年度总结 | 万字雄文
  8. 如何快速上手一款开源软件
  9. Envoy为什么能战胜Ngnix——线程模型分析篇
  10. SpringBoot:Inferred type 'S' for type parameter 'S' is not within its bound