回忆树是树。
具体来说,是n个点n-1条边的无向连通图,点标号为1~n,每条边上有一个字符(出于简化目的,我们认为只有小写字母)。
对一棵回忆树来说,回忆当然是少不了的。
一次回忆是这样的:你想起过往,触及心底…唔,不对,我们要说题目。
这题中我们认为回忆是这样的:给定2个点u,v(u可能等于v)和一个非空字符串s,问从u到v的简单路径上的所有边按照到u的距离从小到大的顺序排列后,边上的字符依次拼接形成的字符串中给定的串s出现了多少次。
对于100100%%%100%的数据,n<=100000,m<=100000n<=100000,m<=100000n<=100000,m<=100000,询问串的总长<=300000<=300000<=300000
我们把s的在u−>vu->vu−>v中的出现拆分成3个类。
1:包含lca,那么只有从lca往u,v走∣S∣|S|∣S∣条边内的所有点可能被涉及,直接跑KMP。
2:uuu为起点往上的链。
3:vvv为终点往下的链。
那么后两者都可以用一条树链上的字符串在AC自动机上的贡献-字符串选有字符在lca以上的贡献得到。
在dfs的时候维护字符串在AC自动机上的位置和贡献就行。
AC Code:
#include<bits/stdc++.h>
#define maxn 600005
#define maxc 26
using namespace std;
int n,m,ans[maxn],loc[maxn];
int st[maxn],ed[maxn];
namespace AC{
int tr[maxn][maxc],tot;
vector<int>G[maxn];
int fa[maxn],id[maxn],dfn;
void insert(char *s,int id){
int u = 0;
for(int i=0,len = strlen(s);i<len;u=tr[u][s[i]-'a'],i++)
if(!tr[u][s[i]-'a']) tr[u][s[i]-'a'] = ++tot;
loc[id] = u;
}
void dfs(int now){
st[now] = ++dfn;
for(int i=0,sz=G[now].size(),v;i<sz;i++)
dfs(v=G[now][i]);
ed[now] = dfn;
}
void Build(