【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

转载于:https://www.cnblogs.com/CQzhangyu/p/7749512.html

【BZOJ4231】回忆树 离线+fail树+KMP相关推荐

  1. SPOJ - DQUERY D-query(莫队/线段树+离线/主席树)

    题目链接:点击查看 题目大意:给出一个由n个数组成的序列,再给出m次查询,每次查询区间[l,r]中有多少个不同的数 题目分析:莫队模板题,直接套板子就好了 有点意思的是函数返回值为布尔类型,然后没有r ...

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

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

  3. fail树(bzoj 3172: [Tjoi2013]单词)

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 4223  Solved: 2051 [Submit][Stat ...

  4. HDU - 7084 Pty loves string kmp + fail树 + 主席树

    传送门 文章目录 题意: 思路: 题意: 给你一个字符串sss,有qqq个询问,每次给x,yx,yx,y代表取sss的前xxx个字符和后yyy个字符拼接起来得到ttt,输出ttt在sss中出现的次数. ...

  5. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 2545  Solved: 1419 [Submit][S ...

  6. bzoj 2434 [Noi2011]阿狸的打字机(AC自动机+fail树+dfs序+树状数组)

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 3521  Solved: 1913 [Submit][S ...

  7. BZOJ 2434: [Noi2011]阿狸的打字机 ACAM+fail树

    title BZOJ 2434 LUOGU 2414 Description 打字机上只有 \(28\) 个按键,分别印有 \(26\) 个小写英文字母和 B.P 两个字母,是这样工作的: 输入小写字 ...

  8. P5357 【模板】AC自动机(二次加强版)(AC自动机建fail树dfs求模式串出现次数)

    P5357 [模板]AC自动机(二次加强版)(AC自动机建fail树dfs求模式串出现次数) 传送门 形式上,AC 自动机基于由若干模式串构成的 Trie 树,并在此之上增加了一些 fail 边:本质 ...

  9. CodeForces - 1437G Death DBMS(AC自动机fail树上树链剖分建线段树/暴跳fail)

    题目链接:点击查看 题目大意:给出 n 个模式串,每个模式串初始时的权值为 0,然后有 m 次操作: 1 i x:将第 i 个模式串的权值修改为 x 2 s:给出一个字符串 s,询问字符串 s 作为主 ...

最新文章

  1. 一张图系列——为什么在DllMain里面创建了线程并Wait会卡死
  2. 【Linux系列】【基础版】第二章 文件、目录管理
  3. 用matlab求残余误差,matlab在测量误差分析中的应用
  4. 关于 Fatal NI connect error 12170
  5. java 课后习题 随机数统计
  6. 孩子哭的时候大人应该怎么办?
  7. spring引入properties变量报错
  8. 年薪百万是社会认同,更是自身价值体现
  9. Python中各种括号的区别、用途及使用方法
  10. 萤火虫(FA)算法(附完整Matlab代码,可直接复制)
  11. web前端数据可视化框架汇总
  12. ktv点歌系统服务器破解,欧凯KTV卡拉OK点歌系统
  13. 房屋租赁合同主要内容是那些
  14. 新电脑安装系统时提示 File:\Boot\BCD Status:0xc000000e 错误解决方案
  15. Joint Pose and Expression Modeling for Facial Expression Recognition 论文翻译
  16. 【html+css】
  17. java 导出word 含表格_poi 导出word,导出表格(复杂表格合并行列)解决方法
  18. MonoRail学习笔记四:MonoRail基本流程分析
  19. 习题 于歆杰 电路_清华大学 电路原理 于歆杰 60讲视频教程
  20. 「鸡尾酒排序」不会?每天一遍,排序再见【超详细】

热门文章

  1. Eclipse 创建 Java 包
  2. 十二. 一步步破解JEB 2.0demo版二
  3. [转]Entity Framework走马观花之把握全局
  4. 超级数学计算机,超级计算器+
  5. linux 防火墙开机启动项,Ubuntu 9.10下实现Firestarter网络防火墙自启动
  6. 网上的一篇spring security详解教程,觉得不错,转过来了
  7. python统计文本中单词出现次数
  8. win7关闭开机启动项_电脑开机全是各种广告?来看看我怎么解决的吧
  9. Html 内联元素、外联元素 和 可变元素
  10. vue 定义全局函数