一个长度为$|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. NYOJ题目77开灯问题

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAsUAAAHXCAIAAADbX7BCAAAgAElEQVR4nO3dvVLrSMAm4L0Jci6E2B

  2. lsof -ntP -i:端口取出 动行程序的PID 然后xargs kill -9 这个进程

    [root@ok ok]# lsof -ntP -i: [root@ok ok]# netstat -lnutp|grep tcp /dnsmasq tcp /sshd tcp ::: :::* LI ...

  3. 15.命令模式(Command Pattern)

    using System; namespace ConsoleApplication8 { class Program { /// <summary> /// 在软件系统中,“行为请求者” ...

  4. 使用pl/sql監控PROCEDURE執行時間

    創建表 CREATE TABLE PROCESS_TIMING_LOG (   PROCESS_NAME       VARCHAR2(50 BYTE),   EXECUTION_DATE     D ...

  5. ZooKeeper 的羊群效应

    一个需要避免的问题是当一个特定的znode 改变的时候ZooKeper 触发了所有watches 的事件. 举个例子,如果有1000个客户端watch 一个znode的exists调用,当这个节点被创 ...

  6. Centos7-mqtt消息中间件mosquitto的安装和配置

    在以前发布的博客"菜鸟是如何打造智能家居系统的"文章最后我提到了使用MQTT协议作为云平台和设备之间的通信协议以达到消息传递的实时性,手机的消息推送也大多基于这种平台,首先搬来一段 ...

  7. BZOJ 1503: [NOI2004]郁闷的出纳员 splay

    1503: [NOI2004]郁闷的出纳员 Description OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作 ...

  8. N种内核注入DLL的思路及实现

    内核注入,技术古老但很实用.现在部分RK趋向无进程,玩的是SYS+DLL,有的无文件,全部存在于内存中.可能有部分人会说:"都进内核了.什么不能干?".是啊,要是内核中可以做包括R ...

  9. hadoop中map和reduce的数量设置问题

    转载http://my.oschina.net/Chanthon/blog/150500 map和reduce是hadoop的核心功能,hadoop正是通过多个map和reduce的并行运行来实现任务 ...

  10. TortoiseSVN菜单项功能说明

    TortoiseSVN是windows下其中一个非常优秀的SVN客户端工具.通过使用它,我们可以可视化的管理我们的版本库.不过由于它只是一个客户端,所以它不能对版本库进行权限管理. TortoiseS ...