【BZOJ4231】回忆树

Description

回忆树是树。
具体来说,是n个点n-1条边的无向连通图,点标号为1~n,每条边上有一个字符(出于简化目的,我们认为只有小写字母)。
对一棵回忆树来说,回忆当然是少不了的。
一次回忆是这样的:你想起过往,触及心底…唔,不对,我们要说题目。
这题中我们认为回忆是这样的:给定2个点u,v(u可能等于v)和一个非空字符串s,问从u到v的简单路径上的所有边按照到u的距离从小到大的顺序排列后,边上的字符依次拼接形成的字符串中给定的串s出现了多少次。

Input

第一行2个整数,依次为树中点的个数n和回忆的次数m。
接下来n-1行,每行2个整数u、v和1个小写字母c,表示回忆树的点u、v之间有一条边,边上的字符为c
接下来2m行表示m次回忆,每次回忆2行:第1行2个整数u、v,第2行给出回忆的字符串s。

Output

对于每次回忆,输出串s出现的次数。

Sample Input

12 3
1 2 w
2 3 w
3 4 x
4 5 w
5 6 w
6 7 x
7 8 w
8 9 w
9 10 x
10 11 w
11 12 w
1 7
wwx
1 12
www
1 12
w

Sample Output

2
0
8

HINT

对于100%的数据,n<=100000,m<=100000,询问串的总长<=300000

题解:一开始想反了,以为要把原树建成AC自动机。。。不可做?

我们将询问分成两段,一段上去的和一段下去的,这样只需要对询问串的正串和反串分别维护AC自动机即可。那么中间的呢?由于总长不超过2K,所以暴力拿出来跑KMP即可。

考虑每一段,我们采用离线的方法,将询问挂链到树的节点上,在上端的点系数为-1,下端的点系数为+1,然后DFS一遍整棵树,每扫到一个点就将这个点在AC自动机中对应点的权值+1。然后考虑在这个点挂链的所有询问,用询问串对应点在fail树中子树的点权和更新答案即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=100010;
int n,m,t1,t2,l1,l2,cnt;
queue<int> Q;
struct AC
{
int tot;
int ch[26],fail;
}a1[maxn*3],a2[maxn*3];
struct Fail_Tree
{
int tot;
AC *a;
vector<int> ch[maxn*3];
int p[maxn*3],q[maxn*3],s[maxn*3];
void dfs(int x)
{
p[x]=++p[0];
for(int i=0;i<(int)ch[x].size();i++) dfs(ch[x][i]);
q[x]=p[0];
}
void build()
{
Q.push(1);
int i,u;
while(!Q.empty())
{
u=Q.front(),Q.pop();
for(i=0;i<26;i++)
{
if(u==1)
{
if(a[u].ch[i]) Q.push(a[u].ch[i]),a[a[u].ch[i]].fail=1;
else a[u].ch[i]=1;
continue;
}
if(!a[u].ch[i])
{
a[u].ch[i]=a[a[u].fail].ch[i];
continue;
}
Q.push(a[u].ch[i]);
a[a[u].ch[i]].fail=a[a[u].fail].ch[i];
}
}
for(i=2;i<=tot;i++) ch[a[i].fail].push_back(i);
dfs(1);
}
inline void updata(int x,int v)
{
for(int i=x;i<=tot;i+=i&-i) s[i]+=v;
}
inline int query(int x)
{
int i,ret=0;
for(i=x;i;i-=i&-i) ret+=s[i];
return ret;
}
}f1,f2;
struct QUERY
{
int a,b,u,v,top,ans;
}q[maxn];
struct node
{
int org,k;
node() {}
node(int a,int b) {org=a,k=b;}
};
vector<node> b1[maxn],b2[maxn];
int val[maxn<<1],next[maxn<<1],to[maxn<<1],head[maxn],fa[20][maxn],Log[maxn],nxt[maxn*3],from[maxn],dep[maxn];
char T[maxn*3],S[maxn];
inline void add(int a,int b,int c)
{
to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
void getfa(int x)
{
for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[0][x])
fa[0][to[i]]=x,dep[to[i]]=dep[x]+1,from[to[i]]=val[i],getfa(to[i]);
}
inline int lca(int a,int b)
{
if(dep[a]<dep[b]) swap(a,b);
int i;
for(i=Log[dep[a]-dep[b]];i>=0;i--) if(dep[fa[i][a]]>=dep[b]) a=fa[i][a];
if(a==b) return a;
for(i=Log[dep[a]];i>=0;i--) if(fa[i][a]!=fa[i][b]) a=fa[i][a],b=fa[i][b];
return fa[0][a];
}
inline int FA(int x,int y)
{
for(int i=Log[y];i>=0;i--) if((1<<i)<=y) y-=(1<<i),x=fa[i][x];
return x;
}
inline int KMP()
{
int i,j,ret=0;
nxt[0]=-1,i=0,j=-1;
while(i<l2)
{
if(j==-1||T[i]==T[j]) nxt[++i]=++j;
else j=nxt[j];
}
i=j=0;
while(i<l1)
{
if(j==-1||S[i]==T[j]) i++,j++;
else j=nxt[j];
if(j==l2) ret++;
}
return ret;
}
void dfs(int x,int r1,int r2)
{
f1.updata(f1.p[r1],1),f2.updata(f2.p[r2],1);
int i,a;
for(i=0;i<(int)b1[x].size();i++)
{
a=b1[x][i].org;
q[a].ans+=b1[x][i].k*(f1.query(f1.q[q[a].a])-f1.query(f1.p[q[a].a]-1));
}
for(i=0;i<(int)b2[x].size();i++)
{
a=b2[x][i].org;
q[a].ans+=b2[x][i].k*(f2.query(f2.q[q[a].b])-f2.query(f2.p[q[a].b]-1));
}
for(i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[0][x]) dfs(to[i],a1[r1].ch[val[i]],a2[r2].ch[val[i]]);
f1.updata(f1.p[r1],-1),f2.updata(f2.p[r2],-1);
}
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar();
return ret*f;
}
int main()
{
n=rd(),m=rd();
int i,j,a,b,u;
memset(head,-1,sizeof(head));
for(i=1;i<n;i++) a=rd(),b=rd(),scanf("%s",T),add(a,b,T[0]-'a'),add(b,a,T[0]-'a');
dep[1]=1,getfa(1);
for(i=2;i<=n;i++) Log[i]=Log[i>>1]+1;
for(j=1;(1<<j)<=n;j++) for(i=1;i<=n;i++) fa[j][i]=fa[j-1][fa[j-1][i]];
t1=t2=1;
for(i=1;i<=m;i++)
{
q[i].u=rd(),q[i].v=rd(),q[i].top=lca(q[i].u,q[i].v);
scanf("%s",T),l2=strlen(T);
for(u=1,j=0;j<l2;j++)
{
b=T[j]-'a';
if(!a1[u].ch[b]) a1[u].ch[b]=++t1;
u=a1[u].ch[b];
}
q[i].a=u;
for(u=1,j=l2-1;j>=0;j--)
{
b=T[j]-'a';
if(!a2[u].ch[b]) a2[u].ch[b]=++t2;
u=a2[u].ch[b];
}
q[i].b=u;
if(q[i].top!=q[i].u&&q[i].top!=q[i].v)
{
b=min(l2-1,dep[q[i].u]-dep[q[i].top]),l1=b;
for(a=0,j=FA(q[i].u,dep[q[i].u]-dep[q[i].top]-b);j!=q[i].top;a++,j=fa[0][j]) S[a]=from[j]+'a';
b=min(l2-1,dep[q[i].v]-dep[q[i].top]),l1+=b;
for(a=l1-1,j=FA(q[i].v,dep[q[i].v]-dep[q[i].top]-b);j!=q[i].top;a--,j=fa[0][j]) S[a]=from[j]+'a';
q[i].ans+=KMP();
}
if(dep[q[i].v]-dep[q[i].top]>=l2)
b1[q[i].v].push_back(node(i,1)),b1[FA(q[i].v,dep[q[i].v]-dep[q[i].top]-l2+1)].push_back(node(i,-1));
if(dep[q[i].u]-dep[q[i].top]>=l2)
b2[q[i].u].push_back(node(i,1)),b2[FA(q[i].u,dep[q[i].u]-dep[q[i].top]-l2+1)].push_back(node(i,-1));
}
f1.a=a1,f2.a=a2,f1.tot=t1,f2.tot=t2;
f1.build(),f2.build();
dfs(1,1,1);
for(i=1;i<=m;i++) printf("%d\n",q[i].ans);
return 0;
}//12 3 1 2 w 2 3 w 3 4 x 4 5 w 5 6 w 6 7 x 7 8 w 8 9 w 9 10 x 10 11 w 11 12 w 1 7 wwx 1 12 www 1 12 w
//8 1 1 2 a 2 3 b 3 4 a 1 5 b 5 6 a 6 7 b 7 8 a 3 7 ab

【BZOJ4231】回忆树 离线+fail树+KMP的更多相关文章

  1. HDU 4605 Magic Ball Game (在线主席树|| 离线 线段树)

    转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove 题意:给出一棵二叉树,每个结点孩子数目为0或者2. ...

  2. Codeforces 1000F One Occurrence 主席树|| 离线+线段树

    One Occurrence 为什么我半年前这么菜呀, 这种场只A三题... 我们在主席树 || 线段树上维护每个数的右边和它一样的数在哪里, 然后就变成了区间求最大值. 注意加进去的时候要把它右边一 ...

  3. HDU4117 GRE WORDS(AC自动机+线段树维护fail树的dfs序)

    Recently George is preparing for the Graduate Record Examinations (GRE for short). Obviously the mos ...

  4. POJ 2104 K-th Number ( 求取区间 K 大值 || 主席树 || 离线线段树)

    题意 : 给出一个含有 N 个数的序列,然后有 M 次问询,每次问询包含 ( L, R, K ) 要求你给出 L 到 R 这个区间的第 K 大是几 分析 : 求取区间 K 大值是个经典的问题,可以使用 ...

  5. hdu4348 To the moon (主席树 || 离线线段树)

    Problem Description Background To The Moon is a independent game released in November 2011, it is a ...

  6. [BZOJ 1535] [Luogu 3426]SZA-Template (KMP+fail树+双向链表)

    [BZOJ 1535] [Luogu 3426]SZA-Template (KMP+fail树+双向链表) 题面 Byteasar 想在墙上涂一段很长的字符,他为了做这件事从字符的前面一段中截取了一段 ...

  7. 【学习笔记】ac自动机&fail树

    定义 解决文本串和多个模式串匹配的问题: 本质是由多个模式串形成的一个字典树,由tie的意义知道:trie上的每一个节点都是一个模式串的前缀: 在trie上加入fail边,一个节点fail边指向这个节 ...

  8. [POI2005][luogu3462] SZA-Template [fail树]

    题面 传送门 思路 首先,我们观察一下这个要求的"模板串",发现它有如下性质: 1.一个模板串$A$是要求的文本串$B$的公共前后缀 2.如果一个模板串$A$有另一个模板串$B$( ...

  9. Trie图和Fail树

    Trie图和AC自动机的区别 Trie图是AC自动机的确定化形式,即把每个结点不存在字符的next指针都补全了.这样做的好处是使得构造fail指针时不需要next指针为空而需要不断回溯. 比如构造ne ...

随机推荐

  1. 让你的jenkins能当奴隶主---说说几种Jenkins build框架

    近期又有几个朋友看了俺的文章询问.怎样让Jenkins能做到分布式.我解释了非常久,发现这也是个非常好的topic,就来博客继续念叨念叨. 这个非常easy,所以这篇文章也就介绍简单点. 首先说下Je ...

  2. hibernate 中HQL查询

    由于比较简单,在此处只写一些HQL语言. 表关系,多对一. CREATE TABLE `user` ( `id` ) NOT NULL AUTO_INCREMENT, `uname` varchar( ...

  3. 蜜果私塾:informix数据库学习合集[不断补充]

    一.infomix使用备忘录     目录结构:     1. 启动与停止命令:      2. 修改数据库编码:      3. 查看informix占用的端口:      4. 使用dbacces ...

  4. XCode5中新建工程后强制使用了ARC,如何去掉?

    打开你的工程,点击目录的工程文件,最顶端蓝色的,然后选择project下你的工程,还是蓝色那项,然后build Settings,然后往下拉,在Apple LLVM 5.0 - Language - ...

  5. 怎么使用Less/Sass编译工具koala

    怎么使用Less/Sass编译工具koala 如何使用Less/Sass编译工具koala 一.SASS调试插件的方法 如需调试功能,请在编译输出的时候输出debug信息,那样解析的css文件中就会包 ...

  6. java学习之实例变量初始化

    实例变量的初始化方法 第一种:通过构造函数进行初始化. 第二种:通过声明实例字段初始化. 第三种:通过对象代码块初始化. 通过构造函数进行初始化方法 通过构造函数进行对象初始化,必须在类中声明一个带参 ...

  7. [转]James Bach:测试人员的角色

    [转]James Bach:测试人员的角色 2015-05-13 以前,我是个开发人员.我不喜欢这个工作,无尽的压力让我疲惫.我几乎从未感觉到自己的工作做得足够好.我从未有过真正的休息.如果我没做好, ...

  8. 华中农业大学校赛--c The Same Color

    Problem C: The Same Color Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 993  Solved: 595[Submit][St ...

  9. web.config中的connectionString里面应该怎么写?

    2009-09-16 10:19信欣玛利 | 浏览 6068 次  网络 就是具体格式.. 谁能举个例子? asp.net 3.5的. 2009-09-16 10:40   提问者采纳   这是用来连 ...

  10. 跟着百度学PHP[8]-setcookie的其他参数学习

    setcookie(name,value,time,path,domain,secure) 第四个参数是cookie的有效路径.顾名思义就是在那个路径下是有效的.当然了其子目录下也是有效的.比如 /h ...