BZOJ4231 : 回忆树
一个长度为$|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 : 回忆树的更多相关文章
- 并不对劲的bzoj4231: 回忆树
题目大意 \(n\)个点的树,每条边上有一个小写字母. 操作:给定2个点\(u\),\(v\)(\(u\)可能等于\(v\))和一个非空字符串\(s\),问从\(u\)到\(v\)的简单路径上的所有边 ...
- 【BZOJ4231】回忆树 离线+fail树+KMP
[BZOJ4231]回忆树 Description 回忆树是树. 具体来说,是n个点n-1条边的无向连通图,点标号为1~n,每条边上有一个字符(出于简化目的,我们认为只有小写字母). 对一棵回忆树来说 ...
- 「模拟赛20180306」回忆树 memory LCA+KMP+AC自动机+树状数组
题目描述 回忆树是一棵树,树边上有小写字母. 一次回忆是这样的:你想起过往,触及心底--唔,不对,我们要说题目. 这题中我们认为回忆是这样的:给定 \(2\) 个点 \(u,v\) (\(u\) 可能 ...
- 【bzoj4231】回忆树
题解: 树上的串匹配,模式串的总长$|S|$,令$\overline {S} $为$S$的反串: 对$S$和$\overline {S} $分别建自动机 $u -> v$可以分成三个部分去统计 ...
- 哈夫曼(huffman)树和哈夫曼编码
哈夫曼树 哈夫曼树也叫最优二叉树(哈夫曼树) 问题:什么是哈夫曼树? 例:将学生的百分制成绩转换为五分制成绩:≥90 分: A,80-89分: B,70-79分: C,60-69分: D,<60 ...
- bzoj AC倒序
Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...
- LOJ#510 北校门外的回忆(找性质+倍增+线段树)
这题一场模拟赛我们出了弱化版(n<=1e6),抄题面给的程序能拿到71分的好成绩 其实后面的29分是加了几个1e9的数据卡人 这糟老头子真是坏得很 正解我们机房看了三天 在这里感谢这篇题解的作者 ...
- LOJ#510. 「LibreOJ NOI Round #1」北校门外的回忆(线段树)
题面 传送门 题解 感谢\(@M\_sea\)的代码我总算看懂题解了-- 这个操作的本质就是每次把\(x\)的\(k\)进制最低位乘\(2\)并进位,根据基本同余芝士如果\(k\)是奇数那么最低位永远 ...
- LOJ 北校门外的回忆 倍增+线段树
正解:倍增+线段树 解题报告: 传送门! $umm$这题有个对正解毫无启发的部分分还有个正解,都挺神仙的所以我都写了趴$QAQ$ 先说部分分 可以考虑把$x$向$x+lowbit(x)$连边,然后当$ ...
随机推荐
- Eclipse主题更改
1. 直接安装color theme eclipse:Help->Install New Software->Work with:Update Site -http://eclipse-c ...
- Codeforces Round #344 (Div. 2)(按位或运算)
Blake is a CEO of a large company called "Blake Technologies". He loves his company very m ...
- React Native实例之房产搜索APP
React Native 开发越来越火了,web app也是未来的潮流, 现在react native已经可以完成一些最基本的功能. 通过开发一些简单的应用, 可以更加熟练的掌握 RN 的知识. 在学 ...
- Feature hashing相关 - 1
考虑典型的文本分类,一个经典的方法就是 分词,扫描所有特征,建立特征词典 重新扫描所有特征,利用特征词典将特征映射到特征空间编号 得到特征向量 学习参数 w 存储学习参数 w , 存储特征映射 ...
- Kl 证明 凸函数
回到随机变量传输问题,假设传输中我们不知道具体 分布情况(unknown),我们用一个已知的分布 ,来模拟它,那么在这种情况下如果我们利用 尽可能高效的编码,那么我们平均需要多少额外的信息量来描述x呢 ...
- Task使用小结
Task是.NET推出数据任务处理的工作类,Task的使用也被越来越多的人讲解,这里仅仅介绍Task的部分使用介绍: 1.Task简单创建 --无返回值 Task.Factory.StartNew(( ...
- HTML CSS微信CSS显示一些总结
微信显示网页是调用腾讯自带的浏览器内核,由于腾讯浏览器内核对css展示效果没有谷歌浏览器好,导致用谷歌浏览器写好的网页,放到微信页面之后,显示的效果就发生变化,所以调整css样式显得那么吃力: 1. ...
- HDU2205 又见回文(区间DP)
题意:给定两个字符串(可能为空串),求这两个串交叉组成新串的子串中的回文串的最大长度. 布尔型变量dp[i][j][k][l]表示串a从i到j,b从k到l能否组成新串,初始化为false,则采取区间动 ...
- WCF消息拦截,利用消息拦截做身份验证服务
本文参考 http://blog.csdn.net/tcjiaan/article/details/8274493 博客而写 添加对信息处理的类 /// <summary> /// 消 ...
- 设计工具 -uml