一个长度为$|S|$的串在树上匹配有两种情况:

1.在LCA处转弯,那么这种情况只有$O(|S|)$次,暴力提取出长度为$2|S|$的链进行KMP即可。

2.不转弯,那么可以拆成两个到根路径的询问。

对所有串的正反串建立AC自动机,求出fail树上每个点的DFS序。

然后DFS原树,记录在AC自动机上走到了哪个点,在那个点$+1$,回溯的时候$-1$。

那么一个询问的答案就是fail树上的子树和,树状数组维护即可。

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

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010,M=600010;
char a[M],b[N];
int n,m,len,i,j,x,y,z,g[N],v[N<<1],w[N<<1],nxt[N<<1],ed,G[N],V[N<<2],W[N<<2],NXT[N<<2];
int size[N],son[N],f[N],fs[N],d[N],top[N],loc[N],seq[N],dfn,ans[N];
inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;}
inline void ADD(int x,int y,int z){V[++ed]=y;W[ed]=z;NXT[ed]=G[x];G[x]=ed;}
void dfs(int x){
size[x]=1;
for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){
f[v[i]]=x,fs[v[i]]=w[i],d[v[i]]=d[x]+1;
dfs(v[i]),size[x]+=size[v[i]];
if(size[v[i]]>size[son[x]])son[x]=v[i];
}
}
void dfs2(int x,int y){
seq[loc[x]=++dfn]=x;top[x]=y;
if(son[x])dfs2(son[x],y);
for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]);
}
inline int lca(int x,int y){
for(;top[x]!=top[y];x=f[top[x]])if(d[top[x]]<d[top[y]])swap(x,y);
return d[x]<d[y]?x:y;
}
inline int kth(int x,int y){
while(d[x]-d[top[x]]<y)y-=d[x]-d[top[x]]+1,x=f[top[x]];
return seq[loc[x]-y];
}
namespace KMP{
int nxt[M];
inline void cross(int x,int y,int&ret){
int t=0,i,j,A=kth(x,d[x]-min(d[z]+len-1,d[x])),B=kth(y,d[y]-min(d[z]+len-1,d[y]));
for(nxt[0]=j=-1,i=1;i<len;nxt[i++]=j){
while(~j&&a[j+1]!=a[i])j=nxt[j];
if(a[j+1]==a[i])j++;
}
for(j=-1;A!=z;A=f[A]){
while(~j&&a[j+1]!=fs[A])j=nxt[j];
if(a[j+1]==fs[A])j++;
if(j==len-1)ret++,j=nxt[j];
}
while(B!=z)b[++t]=fs[B],B=f[B];
while(t){
while(~j&&a[j+1]!=b[t])j=nxt[j];
if(a[j+1]==b[t])j++;
if(j==len-1)ret++,j=nxt[j];
t--;
}
}
}
namespace AC{
int tot,son[M][26],f[M],q[M],g[M],nxt[M],st[M],en[M],dfn,bit[M];
inline void addedge(int x,int y){nxt[y]=g[x];g[x]=y;}
inline int ins0(){
int x=0;
for(int i=0;i<len;i++){
if(!son[x][a[i]])son[x][a[i]]=++tot;
x=son[x][a[i]];
}
return x;
}
inline int ins1(){
int x=0;
for(int i=len-1;~i;i--){
if(!son[x][a[i]])son[x][a[i]]=++tot;
x=son[x][a[i]];
}
return x;
}
void dfs(int x){
st[x]=++dfn;
for(int i=g[x];i;i=nxt[i])dfs(i);
en[x]=dfn;
}
inline void add(int x,int p){for(x=st[x];x<=dfn;x+=x&-x)bit[x]+=p;}
inline int sum(int x){int t=0;for(;x;x-=x&-x)t+=bit[x];return t;}
inline int ask(int x){return sum(en[x])-sum(st[x]-1);}
void make(){
int h=1,t=0,i,j,x;f[0]=-1;
for(i=0;i<26;i++)if(son[0][i])q[++t]=son[0][i];
while(h<=t)for(x=q[h++],i=0;i<26;i++)
if(son[x][i])f[son[x][i]]=son[f[x]][i],q[++t]=son[x][i];
else son[x][i]=son[f[x]][i];
for(i=1;i<=tot;i++)addedge(f[i],i);
dfs(0);
}
}
void dfs3(int x,int y){
AC::add(y,1);
for(int i=G[x];i;i=NXT[i]){
int o=W[i];
if(o>0)ans[o]+=AC::ask(V[i]);else ans[-o]-=AC::ask(V[i]);
}
for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x])dfs3(v[i],AC::son[y][w[i]]);
AC::add(y,-1);
}
int main(){
scanf("%d%d",&n,&m);
for(i=1;i<n;i++)scanf("%d%d%s",&x,&y,a),add(x,y,a[0]-'a'),add(y,x,a[0]-'a');
dfs(1),dfs2(1,1);
for(ed=0,i=1;i<=m;i++){
scanf("%d%d%s",&x,&y,a);len=strlen(a);z=lca(x,y);
for(j=0;j<len;j++)a[j]-='a';
KMP::cross(x,y,ans[i]);
if(d[x]-d[z]>=len){
j=AC::ins1();
ADD(x,j,i);
ADD(kth(x,d[x]-d[z]-len+1),j,-i);
}
if(d[y]-d[z]>=len){
j=AC::ins0();
ADD(y,j,i);
ADD(kth(y,d[y]-d[z]-len+1),j,-i);
}
}
AC::make();
dfs3(1,0);
for(i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}

  

BZOJ4231 : 回忆树的更多相关文章

  1. 并不对劲的bzoj4231: 回忆树

    题目大意 \(n\)个点的树,每条边上有一个小写字母. 操作:给定2个点\(u\),\(v\)(\(u\)可能等于\(v\))和一个非空字符串\(s\),问从\(u\)到\(v\)的简单路径上的所有边 ...

  2. 【BZOJ4231】回忆树 离线+fail树+KMP

    [BZOJ4231]回忆树 Description 回忆树是树. 具体来说,是n个点n-1条边的无向连通图,点标号为1~n,每条边上有一个字符(出于简化目的,我们认为只有小写字母). 对一棵回忆树来说 ...

  3. 「模拟赛20180306」回忆树 memory LCA+KMP+AC自动机+树状数组

    题目描述 回忆树是一棵树,树边上有小写字母. 一次回忆是这样的:你想起过往,触及心底--唔,不对,我们要说题目. 这题中我们认为回忆是这样的:给定 \(2\) 个点 \(u,v\) (\(u\) 可能 ...

  4. 【bzoj4231】回忆树

    题解: 树上的串匹配,模式串的总长$|S|$,令$\overline {S} $为$S$的反串: 对$S$和$\overline {S} $分别建自动机 $u -> v$可以分成三个部分去统计 ...

  5. 哈夫曼(huffman)树和哈夫曼编码

    哈夫曼树 哈夫曼树也叫最优二叉树(哈夫曼树) 问题:什么是哈夫曼树? 例:将学生的百分制成绩转换为五分制成绩:≥90 分: A,80-89分: B,70-79分: C,60-69分: D,<60 ...

  6. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  7. LOJ#510 北校门外的回忆(找性质+倍增+线段树)

    这题一场模拟赛我们出了弱化版(n<=1e6),抄题面给的程序能拿到71分的好成绩 其实后面的29分是加了几个1e9的数据卡人 这糟老头子真是坏得很 正解我们机房看了三天 在这里感谢这篇题解的作者 ...

  8. LOJ#510. 「LibreOJ NOI Round #1」北校门外的回忆(线段树)

    题面 传送门 题解 感谢\(@M\_sea\)的代码我总算看懂题解了-- 这个操作的本质就是每次把\(x\)的\(k\)进制最低位乘\(2\)并进位,根据基本同余芝士如果\(k\)是奇数那么最低位永远 ...

  9. LOJ 北校门外的回忆 倍增+线段树

    正解:倍增+线段树 解题报告: 传送门! $umm$这题有个对正解毫无启发的部分分还有个正解,都挺神仙的所以我都写了趴$QAQ$ 先说部分分 可以考虑把$x$向$x+lowbit(x)$连边,然后当$ ...

随机推荐

  1. git 提交代码的流程

    [root@ok-T IT-DOC]# ls hx-海星-wifi.rd web收藏.txt [root@ok-T IT-DOC]# git status -s ?? "web\346\22 ...

  2. 【ubuntu 】常见错误--Could not get lock /var/lib/dpkg/lock

    ubuntu 常见错误--Could not get lock /var/lib/dpkg/lock 通过终端安装程序sudo apt-get install xxx时出错: E: Could not ...

  3. LightOJ 1030 Discovering Gold(期望 概率)

    正推,到达i的概率为p[i],要注意除了1和n外,到达i的概率并不一定为1 概率表达式为p[i] += p[j] / min(n - j, 6) 从j带过来的期望为exp[i] += exp[j] / ...

  4. 搜索引擎爬虫技术研究(爬虫框架)-WebCollector

    一.简介: https://github.com/CrawlScript/WebCollector/blob/master/README.zh-cn.md 二.使用: <dependency&g ...

  5. Oracle【IT实验室】数据库备份与恢复之四:RMAN(备份与恢复管理器)

    RMAN是ORACLE提供的一个备份与恢复的工具,可以用来备份和还原数据库文件. 归档日志和控制文件.它也可以用来执行完全或不完全的数据库恢复. RMAN可以由命令行接口或者 OEM的 Backup ...

  6. android 入门-android Studio git配置

    以后在整理

  7. 10个很棒的学习Android 开发的网站(转)

    看到江湖旅人 写的<10个很棒的学习iOS开发的网站 - 简书>,所以就忍不住写Android 啦,也希望对大家有帮助.我推荐的网站,都是我在学习Android 开发过程中发现的好网站,给 ...

  8. 在Asp.Net MVC中实现CompareValues标签对Model中的属性进行验证

    在Asp.Net MVC中可以用继承ValidationAttribute的方式,自定制实现Model两个中两个属性值的比较验证 具体应用场景为:要对两个属性值的大小进行验证 代码如下所示: /// ...

  9. indeterminateDrawable

    Android原生控件只有横向进度条一种,而且没法变换样式,比如原生rom的样子很丑是吧,当伟大的产品设计要求更换前背景,甚至纵向,甚至圆弧状的,咋办,比如ok,我们开始吧: 一)变换前背景 先来看看 ...

  10. static之用法

    本文转载于http://www.cnblogs.com/stoneJin/archive/2011/09/21/2183313.html 在C语言中,static的字面意思很容易把我们导入歧途,其实它 ...