显然是用AC自动机

先构建好AC自动机,当B中插入新的串时就在trie上跑,对于当前点,首先这个点所代表的串一定出现过,然后这个点指向的fail也一定出现过.那么我们把每个点fail当作父亲,建一棵fail树,那么到一个点一定会让fail树中这个点到根的路径所有点的答案+1.然后因为在同一个串中多次出现只算一次,那么就需要求这些到根的路径的并集.可以用树链剖分求区间交集做.

但这道题我们只用单点查询,区间修改,可以用树状数组做.因为是求交集,这里有一个trick就是把所有的点按dfs序排序,然后每个点到根+1,相邻的点的lca到根-1,就简单的求出了并集.

我们将区间修改单点查询转化为单点修改区间查询.那么一个点就只用查子树内的就行了.

具体可以看代码.修改的总点数不超过询问的串的总长,所以时间复杂度是O(nlogn)O(nlogn)O(nlogn)

lca是用树剖求的

CODE

#include<bits/stdc++.h>
using namespace std;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &res) {
char ch; int flg = 1; while(!isdigit(ch=getchar()))if(ch=='-')flg=-flg;
for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0'); res*=flg;
}
const int MAXN = 2000005;
const int C = 26;
int n, m, node[MAXN];
int tmr, dfn[MAXN], seq[MAXN], dep[MAXN], son[MAXN], sz[MAXN], top[MAXN], fa[MAXN];
int fir[MAXN], to[MAXN], nxt[MAXN], cnt;
inline void link(int u, int v) { to[cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt++; }
void dfs1(int x) {
dep[x] = dep[fa[x]] + 1; sz[x] = 1;
for(int v, i = fir[x]; ~i; i = nxt[i]) {
dfs1(v=to[i]), sz[x] += sz[v];
if(!(~son[x]) || sz[son[x]] < sz[v])
son[x] = v;
}
}
void dfs2(int x, int tp) {
top[x] = tp; seq[dfn[x]=++tmr] = x;
if(~son[x]) dfs2(son[x], tp);
for(int v, i = fir[x]; ~i; i = nxt[i])
if((v=to[i]) != son[x]) dfs2(v, v);
}
inline int lca(int x, int y) {
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
struct Aho_Corasick {
int ch[MAXN][C], fail[MAXN], tot, q[MAXN];
inline int insert(char *s) {
int r = 0, i = 0, indx;
while(*s) {
if(!ch[r][indx=(*s++)-'a'])
ch[r][indx] = ++tot;
r = ch[r][indx];
}
return r;
}
inline void build() {
int r = 0, head = 0, tail = 0;
fail[q[tail++]=r] = -1;
while(head < tail) {
r = q[head++];
for(int v, indx = 0; indx < C; ++indx)
if((v=ch[r][indx])) {
q[tail++] = v;
fail[v] = (r == 0 ? 0 : ch[fail[r]][indx]);
}
else ch[r][indx] = (r == 0 ? 0 : ch[fail[r]][indx]);
}
memset(fir, -1, sizeof fir);
memset(son, -1, sizeof son);
for(int i = 1; i <= tot; ++i) link(fa[i]=fail[i], i);
dfs1(0); dfs2(0, 0);
}
int T[MAXN];
inline void upd(int x, int val) {
while(x <= tmr) T[x] += val, x += x&-x;
}
inline int qsum(int x) { int re = 0;
while(x) re += T[x], x -= x&-x;
return re;
}
void modify(char *s) {
int r = 0, i = 0, indx, cur = 0;
while(*s) {
r = ch[r][indx=(*s++)-'a'];
q[++cur] = dfn[r];
}
sort(q + 1, q + cur + 1);
cur = unique(q + 1, q + cur + 1) - q - 1;
for(int i = 1; i <= cur; ++i) {
upd(q[i], 1);
if(i > 1) upd(dfn[lca(seq[q[i]], seq[q[i-1]])], -1);
}
}
}trie;
char s[MAXN];
int main() {
read(n);
for(int i = 1; i <= n; ++i)
scanf("%s", s), node[i] = trie.insert(s);
trie.build();
read(m);
int op, x;
while(m--) {
read(op);
if(op == 1) scanf("%s", s), trie.modify(s);
else read(x), x = node[x], printf("%d\n", trie.qsum(dfn[x]+sz[x]-1)-trie.qsum(dfn[x]-1));
}
}

BZOJ 3881[COCI2015]Divljak (AC自动机+dfs序+lca+BIT)的更多相关文章

  1. BZOJ 3881: [Coci2015]Divljak [AC自动机 树链的并]

    3881: [Coci2015]Divljak 题意:添加新文本串,询问某个模式串在多少种文本串里出现过 模式串建AC自动机,考虑添加一个文本串,走到的节点记录下来求树链的并 方法是按dfs序排序去重 ...

  2. bzoj 3881: [Coci2015]Divljak AC自动机

    题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=3881 题解: 这道题我想出了三种做法,不过只有最后一种能过. 第一种: 首先我们把所有的 ...

  3. BZOJ-3881:Divljak (AC自动机+DFS序+树链求并+树状数组)

    Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. “2 x” ...

  4. BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )

    一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状 ...

  5. BZOJ 3881: [Coci2015]Divljak

    3881: [Coci2015]Divljak Time Limit: 20 Sec  Memory Limit: 768 MBSubmit: 553  Solved: 176[Submit][Sta ...

  6. BZOJ2434[Noi2011]阿狸的打字机——AC自动机+dfs序+树状数组

    题目描述 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小 ...

  7. BZOJ3881[Coci2015]Divljak——AC自动机+树状数组+LCA+dfs序+树链的并

    题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...

  8. 【bzoj3881】[Coci2015]Divljak AC自动机+树链的并+DFS序+树状数组

    题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...

  9. BZOJ 3881 [Coci2015]Divljak(AC自动机+树状数组)

    建立AC自动机然后,加入一个串之后考虑这个串的贡献.我们把这个串扔到AC自动机里面跑.最后对经过每一个点到的这个点在fail树的根的路径上的点有1的贡献.求链的并,我们把这些点按DFS序排序,然后把每 ...

随机推荐

  1. setsockopt用法详解

    最近做的一个程序用到了IOCP通信模型,里面用到了setsockopt对套接字进行设置,看源代码的时候最setsockopt函数很不理解,看了msdn以后还是不太明白这个函数的用法,于是就到网上找了一 ...

  2. mysql数据库设计字符类型及长度

    1.数字类型 小数的我就不聊了,因为有小数点的一般都是用字符串保存.关于整数,有几种可以选TINYINT.SMALLINT.MEDIUMINT.INT和BIGINT,分别占1.2.4.8字节.如果无符 ...

  3. 易混乱javascript知识点简要记录

    一.== vs === ==在做判断时,如果对比双方的类型不一样的话,就会进行类型转换 假如我们需要对比 x 和 y 是否相同,就会进行如下判断流程: 首先会判断两者类型是否相同.相同的话就是比大小了 ...

  4. SpringBoot起飞系列-拦截器和统一错误处理(七)

    一.前言 在前边部分我们已经学会了基本的web开发流程,在web开发中,我们通常会对请求做统一处理,比如未登录的用户要拦截掉相关请求,报错页面统一显示等等,这些都需要配置,可以大大简化我们的代码,实现 ...

  5. 拜托,别再问我 QPS、TPS、PV、UV、GMV、IP、RPS 好吗?

    关于 QPS.TPS.PV.UV.GMV.IP.RPS 这些词语,看起来好像挺专业.但实际上,我认为是这是每个程序员必懂的知识点了,你可以搞不懂它们怎么计算的,但是你最少要知道它们分别代表什么意思吧? ...

  6. Neo4j Cypher语法(一)

    目录 Cypher手册详解 1 背景 2 唯一性 3 语法 3.1 命名规则 3.2 表达式 3.3 变量与保留关键字 3.4 参数 3.5 操作符 3.6 模式 3.7 列表 Cypher手册详解 ...

  7. 怎样理解 instanceof

    instanceof 运算符用来判断一个对象在其原型链中是否存在一个构造函数的 prototype 属性. 也就是说, instanceof 判断的实际上是某个对象是否为某个构造函数的实例, 因为es ...

  8. Bootstrap3基础教程 01 概述

    移动设备优先是 Bootstrap 3 的最显著的变化. 基础的页面: <!DOCTYPE html> <html> <head> <meta charset ...

  9. C# 使用Emit实现动态AOP框架 进阶篇之优化

    目  录 C# 使用Emit实现动态AOP框架 (一) C# 使用Emit实现动态AOP框架 (二) C# 使用Emit实现动态AOP框架 (三) C# 使用Emit实现动态AOP框架 进阶篇之异常处 ...

  10. 搭建自己的框架WedeNet(三)

    WedeNet2018.BussinessLogic-业务逻辑层:结构如下: 基类: using System; using System.Collections.Generic; using Sys ...