【bzoj3881】[Coci2015]Divljak AC自动机+树链的并+DFS序+树状数组
题目描述
输入
输出
样例输入
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序+树状数组的更多相关文章
- BZOJ3881[Coci2015]Divljak——AC自动机+树状数组+LCA+dfs序+树链的并
题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...
- 【BZOJ】2819: Nim(树链剖分 / lca+dfs序+树状数组)
题目 传送门:QWQ 分析 先敲了个树链剖分,发现无法AC(其实是自己弱,懒得debug.手写栈) 然后去学了学正解 核心挺好理解的,$ query(a) $是$ a $到根的异或和. 答案就是$ l ...
- Codeforces Round #200 (Div. 1) D Water Tree 树链剖分 or dfs序
Water Tree 给出一棵树,有三种操作: 1 x:把以x为子树的节点全部置为1 2 x:把x以及他的所有祖先全部置为0 3 x:询问节点x的值 分析: 昨晚看完题,马上想到直接树链剖分,在记录时 ...
- BZOJ 3881: [Coci2015]Divljak [AC自动机 树链的并]
3881: [Coci2015]Divljak 题意:添加新文本串,询问某个模式串在多少种文本串里出现过 模式串建AC自动机,考虑添加一个文本串,走到的节点记录下来求树链的并 方法是按dfs序排序去重 ...
- 【BZOJ-3881】Divljak AC自动机fail树 + 树链剖分+ 树状数组 + DFS序
3881: [Coci2015]Divljak Time Limit: 20 Sec Memory Limit: 768 MBSubmit: 508 Solved: 158[Submit][Sta ...
- BZOJ-3881:Divljak (AC自动机+DFS序+树链求并+树状数组)
Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. “2 x” ...
- bzoj 3881: [Coci2015]Divljak AC自动机
题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=3881 题解: 这道题我想出了三种做法,不过只有最后一种能过. 第一种: 首先我们把所有的 ...
- BZOJ 3881[COCI2015]Divljak (AC自动机+dfs序+lca+BIT)
显然是用AC自动机 先构建好AC自动机,当B中插入新的串时就在trie上跑,对于当前点,首先这个点所代表的串一定出现过,然后这个点指向的fail也一定出现过.那么我们把每个点fail当作父亲,建一棵f ...
- 【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)
http://www.lydsy.com/JudgeOnline/problem.php?id=1146 第一种做法(时间太感人): 第二种做法(rank5,好开心) ================ ...
随机推荐
- POJ 1144 Network(tarjan 求割点个数)
Network Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 17016 Accepted: 7635 Descript ...
- kubernetes 禁用虚拟内存 swapoff -a ----- 顺便复习sed 命令
1.如果不关闭swap,就会在kubeadm初始化Kubernetes的时候报错,如下图: [ERROR Swap]: running with swap on is not supported. P ...
- Beautifulsoap - request 网络爬虫 (转)
http://www.cnblogs.com/jiayongji/p/7118939.html (转) python爬虫系列(2)—— requests和BeautifulSoup库的基本用法
- 让win7变成无线路由(需要用管理员权限打开)最后完善.rar
让win7变成无线路由(需要用管理员权限打开)最后完善.bat @ECHO OFF CLS color 0a netsh wlan show drivers ECHO.★★★★★★★★★★★★★★★★ ...
- Docker一键部署Hadoop心得(二)
今天在运行MapReduce程序时,虽然wordcount实例运行成功了,但后面出现了重新使用历史服务器失败的错误 17/12/22 13:33:19 INFO ipc.Client: Retryin ...
- centos安装redis并设置开机启动
1.通过yum安装: yum install redis 2.设置redis.conf中daemonize为yes.设置密码: requirepass 3.安装完后的启动脚本是完善的,/etc/ini ...
- R语言的数据输入
既然了解了R语言的基本数据类型,那么如何将庞大的数据送入R语言进行处理呢?送入的数据又是如何在R语言中进行存储的呢?处理这些数据的方法又有那些呢?下面我们一起来探讨一下. 首先,数据输入最直接最直观的 ...
- WFP page navigator control
WPF navigator UI: <Grid x:Class="WpfApplication2.PagerNav" xmlns="http://schemas.m ...
- 20155306 白皎 《网络攻防》 EXP7 网络欺诈技术防范
20155306 白皎 <网络攻防> EXP7 网络欺诈技术防范 问题回答 (1)通常在什么场景下容易受到DNS spoof攻击 局域网内的攻击以及连接公众场所的共享wifi (2)在日常 ...
- Java实现Zip压缩包解压
对zip压缩包的解压是比较常见的应用场景,java代码的实现也很简单.废话不多说,直接上代码吧 一.代码 /** * zip解压 * @param srcFile zip源文件 * @para ...