题目大意:

http://www.lydsy.com/JudgeOnline/problem.php?id=3881

题解:

这道题我想出了三种做法,不过只有最后一种能过。


第一种:

首先我们把所有的操作离线下来,把所有的字符串全部插入,构建fail树

对于每个字符串记录插入时间,并设S集合中的字符串的插入时间为无限大

然后对于每一个询问,查找fail树中以\(S_x\)为根的子树里有多少插入时间小于询问时间的节点。

这一步可以处理出dfs序后用可持久化线段树搞。

但是它TLE了,我觉得这种做法完全没有问题啊,为什么还是TLE了....


第二种:

首先只插入S集合中所有的字符串,然后构建AC自动机,求出fail树

然后对于每一次向T集合中的插入,我们把这个串扔到自动机里

这时候我们发现,这个串仍到AC自动机里后经过的所有的状态的对应的串都多出现了一次

所以我们知道,经过的所有的节点及其到fail树的根的路径上的点代表的串都多出现了一次

所以将所有经过的状态的节点拿出来,按照dfs序排序,将所有节点到根的路径上的节点都+1

相邻节点的lca到根的路径上的节点-1.(dfs序最大的和dfs最小的不相邻)

这个操作我们用树链剖分套线段树即可\(O(nlog^2n)\) TLE


第三种:

仔细考虑一下发现上面的操作可以用单点修改,子树查询的方式搞掉

所以在dfs序上用树状数组就好了啊....

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 2000010;
int ch[maxn][26],nodecnt;
int mp[maxn];
char s[2000010];
inline void insert(const int &pos){
int len = strlen(s),nw = 0;
for(int i=0;i<len;++i){
int c = s[i] - 'a';
if(ch[nw][c] == 0) ch[nw][c] = ++ nodecnt;
nw = ch[nw][c];
}mp[pos] = nw;
}
int fail[maxn],q[maxn];
inline void build(){
int l = 0,r = -1;
for(int c=0;c<26;++c){
if(ch[0][c]){
fail[ch[0][c]] = 0;
q[++r] = ch[0][c];
}
}
while(l <= r){
int u = q[l++];
for(int c=0;c<26;++c){
int t = ch[fail[u]][c];
if(!ch[u][c]) ch[u][c] = t;
else{
fail[ch[u][c]] = t;
q[++r] = ch[u][c];
}
}
}
}
struct Edge{
int to,next;
}G[maxn<<1];
int head[maxn],cnt;
void add(const int &u,const int &v){
G[++cnt].to = v;
G[cnt].next = head[u];
head[u] = cnt;
}
#define v G[i].to
int fa[maxn],ind[maxn],oud[maxn],dfs_clock;
int siz[maxn],son[maxn],dep[maxn];
void dfs(const int &u){
siz[u] = 1;
for(int i = head[u];i;i=G[i].next){
if(v == fa[u]) continue;
fa[v] = u;
dep[v] = dep[u] + 1;
dfs(v);
siz[u] += siz[v];
if(siz[son[u]] < siz[v]) son[u] = v;
}
}
int top[maxn];
void dfs(const int &u,const int &tp){
top[u] = tp;ind[u] = ++dfs_clock;
if(son[u]) dfs(son[u],tp);
for(int i = head[u];i;i=G[i].next){
if(v == son[u] || v == fa[u]) continue;
dfs(v,v);
}oud[u] = dfs_clock;
}
#undef v
inline int lca(int u,int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u,v);
u = fa[top[u]];
}return dep[u] < dep[v] ? u : v;
}
#define lowbit(x) (x&-x)
int c[maxn+100];
inline void modify(int x,int y){
for(;x <= dfs_clock + 10;x += lowbit(x)) c[x] += y;
}
inline int query(int x){
int ret = 0;
for(;x;x-=lowbit(x)) ret += c[x];
return ret;
}
int a[maxn];
inline bool cmp(const int &i,const int &j){
return ind[i] < ind[j];
}
inline void find(){
int len = strlen(s),nw = 0;
a[0] = 0;
for(int i = 0;i<len;++i){
nw = ch[nw][s[i] - 'a'];
a[++a[0]] = nw+1;
}sort(a+1,a+a[0]+1,cmp);
int p = 1;
for(int i=1;i<=a[0];++i){
if(a[i] != a[i-1] || i == 1) a[p++] = a[i];
}a[0] = p-1;
for(int i=1;i<=a[0];++i){
modify(ind[a[i]],1);
if(i != a[0]) modify(ind[lca(a[i],a[i+1])],-1);
}
}
int main(){
int n;read(n);
for(int i=1;i<=n;++i){
scanf("%s",s);
insert(i);
}build();
for(int i=1;i<=nodecnt;++i) add(fail[i]+1,i+1);
dfs(1);dfs(1,1);
int m;read(m);
int op;
while(m--){
read(op);
if(op == 1){
scanf("%s",s);
find();
}else{
read(op);
printf("%d\n",query(oud[mp[op]+1]) - query(ind[mp[op]+1]-1));
}
}
getchar();getchar();
return 0;
}

bzoj 3881: [Coci2015]Divljak AC自动机的更多相关文章

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

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

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

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

  3. BZOJ 3881: [Coci2015]Divljak

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

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

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

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

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

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

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

  7. BZOJ 3881 [COCI2015]Divljak (Trie图+Fail树+树链的并+树状数组维护dfs序)

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

  8. bzoj 3881 [Coci2015]Divljak——LCT维护parent树链并

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3881 对 S 建 SAM ,每个 T 会让 S 的 parent 树的链并答案+1:在 T ...

  9. bzoj 3881 [Coci2015]Divljak fail树+树链的并

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

随机推荐

  1. DrawRightEditText自定义EditText实现有内容时右侧图标按钮显示无内容时右侧图标按钮隐藏加上为空时晃动动画(二)

    经过大神指导,上面封装的还不够全面,触摸事件应该也放进自定义中去,那么问题来了,怎么区分呢!,这就涉及到了自定义属性的介绍了 我通过设置属性来判断在onTouch事件中应该进行什么操作,接下来看看改良 ...

  2. poj1061(extendgcd)

    看完题目后,题目要求: 设时间为t (x+mt)%L = (y+nt)%L ( x-y + (m-n)*t )= k*L (k是整数,可为负) 然后就是经典的 xa+yb=c 求解x,y的经典题目了. ...

  3. 【BZOJ2115】[Wc2011] Xor 高斯消元求线性基+DFS

    [BZOJ2115][Wc2011] Xor Description Input 第一行包含两个整数N和 M, 表示该无向图中点的数目与边的数目. 接下来M 行描述 M 条边,每行三个整数Si,Ti ...

  4. 洛谷2704 [NOI2001]炮兵阵地

    题目戳这里 Solution 状压DP很好的入门题,用熟练位运算貌似也没那么难. 首先分析一下题目: 看见n=100,m=10,立马就想到了状压,看起来也像DP,所以我们还是采用行号为阶段的状压DP. ...

  5. BZOJ1791: [Ioi2008]Island 岛屿

    BZOJ1791: [Ioi2008]Island 岛屿 Description 你将要游览一个有N个岛屿的公园. 从每一个岛i出发,只建造一座桥. 桥的长度以Li表示. 公园内总共有N座桥. 尽管每 ...

  6. js格式化货币金额

    /* 格式化金额, s : 金额 n : 保留位数 */ function formatMoney(s, n) { n = n > 0 && n <= 20 ? n : 2 ...

  7. HDU - 1114 Piggy-Bank 【完全背包】

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1114 题意 给出一个储钱罐 不知道里面有多少钱 但是可以通过重量来判断 先给出空储钱罐的重量 再给出装 ...

  8. maven导入项目时,缺少部分source folder

    今天导入公司的maven项目时,少了一些source folder,运行启动正常,但是页面打不开,找不到对应的目录文件,使用maven更新项目,重启编辑器也无效. 问题描述如图所示,缺少了图中的2个目 ...

  9. Data Structure Array: Largest subarray with equal number of 0s and 1s

    http://www.geeksforgeeks.org/largest-subarray-with-equal-number-of-0s-and-1s/ #include <iostream& ...

  10. 第二十二篇、IO多路复用 一

    一.简介io多路复用 可以监听多个文件描述符(socket对象)(文件句柄),一旦文件句柄出现变化,就会感知到 Linux中的 select,poll,epoll(内核2.6以上) 都是IO多路复用的 ...