题目描述

Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的。
接下来会发生q个操作,操作有两种形式:
“1 P”,Bob往自己的集合里添加了一个字符串P。
“2 x”,Alice询问Bob,集合T中有多少个字符串包含串S_x。(我们称串A包含串B,当且仅当B是A的子串)
Bob遇到了困难,需要你的帮助。

输入

第1行,一个数n;
接下来n行,每行一个字符串表示S_i;
下一行,一个数q;
接下来q行,每行一个操作,格式见题目描述。

输出

对于每一个Alice的询问,帮Bob输出答案。

样例输入

3
a
bc
abc
5
1 abca
2 1
1 bca
2 2
2 3

样例输出

1
2
1


题解

AC自动机+树链的并+DFS序+树状数组

P的子串体现为前缀的后缀,某个前缀的所有后缀在AC自动机上体现为:Trie树上该前缀对应节点的fail树到根节点的链上节点。

因此对所有S串建立AC自动机,求出fail树,那么添加一个P串,它所包含的S串的范围就是P在Trie树上每个位置(P的每个前缀)fail树上到根节点所覆盖的所有节点,即把树链的并+1。

把所有位置按照DFS序排序,每个点到根节点路径上+1,每相邻两点LCA到根节点路径上-1。查询就是查单点权值。需要支持:到根节点的路径加、单点求值,差分后变为单点加、子树求值,使用DFS将子树转化为区间,再用树状数组维护区间和即可。

时间复杂度 $O(n\log n)$ 。

#include <queue>
#include <cstdio>
#include <algorithm>
#define N 100010
#define M 2000010
using namespace std;
queue<int> q;
int c[M][26] , fail[M] , tot = 1 , pos[N] , head[M] , to[M] , next[M] , cnt , fa[M][20] , deep[M] , log[M] , vp[M] , lp[M] , tp , f[M] , val[M] , tv;
char str[M];
void build()
{
int x , i;
for(i = 0 ; i < 26 ; i ++ ) c[0][i] = 1;
q.push(1);
while(!q.empty())
{
x = q.front() , q.pop();
for(i = 0 ; i < 26 ; i ++ )
{
if(c[x][i]) fail[c[x][i]] = c[fail[x]][i] , q.push(c[x][i]);
else c[x][i] = c[fail[x]][i];
}
}
}
inline void add(int x , int y)
{
to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
int i;
vp[x] = ++tp;
for(i = 1 ; i <= log[deep[x]] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
for(i = head[x] ; i ; i = next[i]) fa[to[i]][0] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
lp[x] = tp;
}
inline int lca(int x , int y)
{
int i;
if(deep[x] < deep[y]) swap(x , y);
for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
if(deep[x] - deep[y] >= (1 << i))
x = fa[x][i];
if(x == y) return x;
for(i = log[deep[x]] ; ~i ; i -- )
if(deep[x] >= (1 << i) && fa[x][i] != fa[y][i])
x = fa[x][i] , y = fa[y][i];
return fa[x][0];
}
inline void fix(int x , int a)
{
int i;
for(i = x ; i <= tp ; i += i & -i) f[i] += a;
}
inline int query(int x)
{
int i , ans = 0;
for(i = x ; i ; i -= i & -i) ans += f[i];
return ans;
}
bool cmp(int a , int b)
{
return vp[a] < vp[b];
}
int main()
{
int n , m , i , j , t , opt , x;
scanf("%d" , &n);
for(i = 1 ; i <= n ; i ++ )
{
scanf("%s" , str) , t = 1;
for(j = 0 ; str[j] ; j ++ )
{
if(!c[t][str[j] - 'a']) c[t][str[j] - 'a'] = ++tot;
t = c[t][str[j] - 'a'];
}
pos[i] = t;
}
build();
for(i = 2 ; i <= tot ; i ++ ) add(fail[i] , i) , log[i] = log[i >> 1] + 1;
dfs(1);
scanf("%d" , &m);
while(m -- )
{
scanf("%d" , &opt);
if(opt == 1)
{
scanf("%s" , str) , t = 1 , tv = 0;
for(i = 0 ; str[i] ; i ++ ) t = c[t][str[i] - 'a'] , val[++tv] = t;
sort(val + 1 , val + tv + 1 , cmp);
for(i = 1 ; i <= tv ; i ++ ) fix(vp[val[i]] , 1);
for(i = 1 ; i < tv ; i ++ ) fix(vp[lca(val[i] , val[i + 1])] , -1);
}
else scanf("%d" , &x) , printf("%d\n" , query(lp[pos[x]]) - query(vp[pos[x]] - 1));
}
return 0;
}

【bzoj3881】[Coci2015]Divljak AC自动机+树链的并+DFS序+树状数组的更多相关文章

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

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

  2. 【BZOJ】2819: Nim(树链剖分 / lca+dfs序+树状数组)

    题目 传送门:QWQ 分析 先敲了个树链剖分,发现无法AC(其实是自己弱,懒得debug.手写栈) 然后去学了学正解 核心挺好理解的,$ query(a) $是$ a $到根的异或和. 答案就是$ l ...

  3. Codeforces Round #200 (Div. 1) D Water Tree 树链剖分 or dfs序

    Water Tree 给出一棵树,有三种操作: 1 x:把以x为子树的节点全部置为1 2 x:把x以及他的所有祖先全部置为0 3 x:询问节点x的值 分析: 昨晚看完题,马上想到直接树链剖分,在记录时 ...

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

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

  5. 【BZOJ-3881】Divljak AC自动机fail树 + 树链剖分+ 树状数组 + DFS序

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

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

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

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

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

  8. BZOJ 3881[COCI2015]Divljak (AC自动机+dfs序+lca+BIT)

    显然是用AC自动机 先构建好AC自动机,当B中插入新的串时就在trie上跑,对于当前点,首先这个点所代表的串一定出现过,然后这个点指向的fail也一定出现过.那么我们把每个点fail当作父亲,建一棵f ...

  9. 【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1146 第一种做法(时间太感人): 第二种做法(rank5,好开心) ================ ...

随机推荐

  1. Android ListView自定义分割线 header 和footer设置没有页眉和页脚

    ListView.setFooterDividersEnabled(false);//设置listview无页脚 ListView.setHeaderDividersEnabled(false);// ...

  2. HDU 1421 搬寝室(经典DP,值得经常回顾)

    搬寝室 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status D ...

  3. 实测—fft IP核使用(包括ifft的配置使用)

    Vivado xilinx fft9.0 使用笔记: ****注 仿真实测1024点的转换需要经过1148个时钟周期才能得到转换结果: 模块配置信号含义请参考pg109文档手册(写的贼烂会看晕),不详 ...

  4. 20155202张旭 Exp6 信息收集与漏洞扫描

    20155202张旭 Exp6 信息收集与漏洞扫描 一.实践目标与内容 1.实践目标: 掌握信息搜集的最基础技能. 具体有: 各种搜索技巧的应用 DNS IP注册信息的查询 基本的扫描技术:主机发现. ...

  5. 2017-2018-2 20155231《网络对抗技术》实验八: WEB基础实验

    2017-2018-2 20155231<网络对抗技术>实验八:Web基础 实验要求: Web前端HTML(0.5分) 能正常安装.启停Apache.理解HTML,理解表单,理解GET与P ...

  6. 20155311《网络对抗》Web基础

    20155311<网络对抗>Web基础 实验过程 Web前端:HTML 使用netstat -aptn查看80端口是否被占用(上次实验设置为Apache使用80端口),如果被占用了就kil ...

  7. JavaEE笔记(十二)

    代理的三种配置 beans配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=& ...

  8. libgdx学习记录19——图片动态打包PixmapPacker

    libgdx中,opengl 1.x要求图片长宽必须为2的整次幂,一般有如下解决方法 1. 将opengl 1.x改为opengl 2.0.(libgdx 1.0版本后不支持1.x,当然不存在这个问题 ...

  9. 8、Dockerfile介绍和最佳实践

    一.Dockerfile 概念 1.Dockerfile是什么 Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序.库.资源.配置等文件外,还包含了一些为运行时准备的一些配置参数(如 ...

  10. Java英文单词Java基础常见英语词汇

    Java英文单词Java基础常见英语词汇(共70个)                                                                          ...