这种题啃腚是用能解决一切树上路径问题的点分治。
考虑当前分治中心为
u
u
时怎样处理子树内经过 的合法路径数量。
从
u
u
开始进行一遍 dfs ,
处理出对于 的子树内每个点
v
v
, 的 hash 值和
u−>v
u
−
>
v
的 hash 值。
然后对于每条路径
v−>u
v
−
>
u
(记长度为
len
l
e
n
,
lenmodm=k
l
e
n
mod
m
=
k
),
如果长度为
len−k
l
e
n
−
k
的前缀不是模式串的重复串,或者长度为
k
k
的后缀不是模式串的前缀,那么 这条路径对于
u
u
是没有用的。
对于 也差不多。
然后用一个计数器,统计下
lenmodm
l
e
n
mod
m
为从
0
0
到 的
u−>v
u
−
>
v
和
v−>u
v
−
>
u
路径数目,然后乱搞即可。
(注意要去除
u
u
和 来自同一子树时带来的多余路径)
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Edge(u) for (int e = adj[u], v; e; e = nxt[e])
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e6 + 5, M = N << 1;
int n, m, ecnt, nxt[M], adj[N], go[M], cnt[N], sze[N], G, maxs[N], len, ks[N],
que[N], dep[N], fa[N], de[N], Ti;
char ch[N], s[N]; ull pre[N], suf[N], pw[N], times[N],
up[N], down[N]; bool vis[N];
void add_edge(int u, int v) {
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}
bool ispre(int u) {
int a = dep[u] / m, b = de[u];
return times[a] * pw[b] + pre[b] == up[u];
}
bool issuf(int u) {
int a = dep[u] / m, b = de[u];
return suf[m - b + 1] * pw[dep[u] - b] + times[a] == down[u];
}
void dfs1(int u, int fu) {
maxs[u] = 0; sze[u] = 1; Edge(u) {
if ((v = go[e]) == fu || vis[v]) continue;
dfs1(v, u); sze[u] += sze[v]; maxs[u] = max(maxs[u], sze[v]);
}
}
void dfs2(int r, int u, int fu) {
maxs[u] = max(maxs[u], sze[r] - sze[u]);
if (maxs[u] < maxs[G]) G = u; Edge(u) {
if ((v = go[e]) == fu || vis[v]) continue;
dfs2(r, v, u);
}
}
void calcG(int u) {dfs1(u, 0); G = u; dfs2(u, u, 0);}
ll calc(int l, ull h1, ull h2, int R) {
int i; down[que[len = 1] = R] = h2 * 31 + (ch[R] - 'A');
up[R] = (ch[R] - 'A') * pw[l - 1] + h1; dep[R] = de[R] = l; fa[R] = 0;
Ti++; For (i, 1, len) {
int u = que[i]; Edge(u) {
if ((v = go[e]) == fa[u] || vis[v]) continue;
down[que[++len] = v] = down[u] * 31 + (ch[v] - 'A');
up[v] = pw[dep[u]] * (ch[v] - 'A') + up[u];
dep[v] = dep[fa[v] = u] + 1; de[v] = de[u] == m - 1 ? 0 : de[u] + 1;
}
if (issuf(u)) {
int p = de[u]; if (ks[p] < Ti) cnt[p] = 1, ks[p] = Ti;
else cnt[p]++;
}
}
ll ans = 0; For (i, 1, len) {
int u = que[i];
if (!ispre(u)) continue;
int z = m - de[u] + 1, r = z >= m ? z - m : z; ans +=
ks[r] == Ti ? cnt[r] : 0;
}
return ans;
}
ll solve(int u) {
calcG(u); ll ans = calc(1, 0, 0, G); vis[G] = 1;
Edge(G) if (!vis[v = go[e]])
ans -= calc(2, ch[G] - 'A', ch[G] - 'A', v);
Edge(G) if (!vis[v = go[e]]) ans += solve(v);
return ans;
}
void work() {
int i, x, y; ecnt = 0; n = read(); m = read(); scanf("%s", ch + 1);
For (i, 1, n) adj[i] = 0, vis[i] = 0; For (i, 1, n - 1) x = read(),
y = read(), add_edge(x, y); scanf("%s", s + 1);
pre[0] = suf[m + 1] = 0;
For (i, 1, m) pre[i] = pre[i - 1] * 31 + (s[i] - 'A');
Rof (i, m, 1) suf[i] = suf[i + 1] + pw[m - i] * (s[i] - 'A');
times[0] = 0; For (i, 1, n) times[i] = times[i - 1] * pw[m] + pre[m];
printf("%lld\n", solve(1));
}
int main() {
int i, T = read(); pw[0] = 1;
For (i, 1, 1000000) pw[i] = pw[i - 1] * 31;
while (T--) work(); return 0;
}