P4075 [SDOI2016]模式字符串

能字符串哈希为什么要写高科技啊。

点分治之,考虑一个子树 V 的贡献。

考虑如何判断 iV 到分治中心 u 的路径能否成为“S 的重复”的前缀 / 后缀。

先序遍历 V,动态地维护 u 到当前遍历到的点 i 的路径形成的字符串 T

进入一个点相当于在 T 后添加一个字符,而退出一个点相当于在 T 后删除一个字符。

以后缀为例,维护 Fi 表示当前 T[1i] 能否成为“S 的重复”的后缀(F0=1),则

Fi={[T[1i]=S[mi+1m]]i<m[Fim=1T[im+1i]=S]im

先序遍历到 i 后转移当前 S 的哈希值和 Fdi,记录此时的 Fdi 表示 iu 的路径能否成为“S 的重复”的后缀。

然后套路地记录 C0/1,i 表示长度为 i 的能成为“S 的重复”的前缀 / 后缀的路径数量,

处理 V 子树时对其中每条长度为 j 的能成为“S 的重复”的前缀 / 后缀的路径,其贡献为 C1/0,(m+1jmodm)modm

(对前缀找后缀,对后缀找前缀)

然后把 V 的信息合并进 C 即可。

#include <cstdio>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
struct E
{
    int v, t;
} e[2000050];
__gnu_pbds::gp_hash_table<int, int> C[2];
char a[1000050], o[1000050];
bool b[1000050], F[1000050][2], W[1000050][2];
unsigned long long q, P[1000050], H[1000050], Z[1000050][2];
int T, n, m, c, l, R, s[1000050], p[1000050], d[1000050], D[1000050], h[1000050];
void A(int u, int v)
{
    e[++c] = {v, h[u]};
    h[u] = c;
}
void X(int u, int k, int t)
{
    s[u] = 1;
    p[u] = 0;
    for (int i = h[u], v; i; i = e[i].t)
        if (!b[v = e[i].v] && v != k)
            X(v, u, t), s[u] += s[v], p[u] = max(p[u], s[v]);
    if (p[R] > (p[u] = max(p[u], t - s[u])))
        R = u;
}
void Y(int u, int k)
{
    H[d[D[l++] = u]] = H[d[u] - 1] * 233 + a[u];
    if (d[u] >= m)
        W[u][0] = F[d[u]][0] = F[d[u] - m][0] & H[d[u]] - H[d[u] - m] * P[m] == Z[m][0],
        W[u][1] = F[d[u]][1] = F[d[u] - m][1] & H[d[u]] - H[d[u] - m] * P[m] == Z[m][1];
    else
        W[u][0] = F[d[u]][0] = H[d[u]] == Z[m][0] - Z[m - d[u]][0] * P[d[u]],
        W[u][1] = F[d[u]][1] = H[d[u]] == Z[m][1] - Z[m - d[u]][1] * P[d[u]];
    for (int i = h[u], v; i; i = e[i].t)
        if (!b[v = e[i].v] && v != k)
            d[v] = d[u] + 1, Y(v, u);
}
void Q(int u, int k)
{
    H[1] = a[u];
    W[u][0] = F[1][0] = C[0][1] = a[u] == o[b[u] = 1];
    W[u][1] = F[1][1] = C[1][1] = a[u] == o[m];
    for (int i = h[u], v; i; i = e[i].t)
        if (!b[v = e[i].v] && v != k)
        {
            d[v] = 2;
            Y(v, u);
            for (int j = 0; j < l; ++j)
                q += W[D[j]][0] * C[1][(m + 1 - d[D[j]] % m) % m] + W[D[j]][1] * C[0][(m + 1 - d[D[j]] % m) % m];
            for (int j = 0; j < l; ++j)
                C[0][d[D[j]] % m] += W[D[j]][0], C[1][d[D[j]] % m] += W[D[j]][1];
            l = 0;
        }
    C[0].clear();
    C[1].clear();
    for (int i = h[u], v; i; i = e[i].t)
        if (!b[v = e[i].v] && v != k)
            R = 0, X(v, u, s[v]), Q(R, u);
}
int main()
{
    p[0] = 1e9;
    F[0][0] = F[0][1] = 1;
    for (int i = P[0] = 1; i <= 1e6; ++i)
        P[i] = P[i - 1] * 233;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d%d%s", &n, &m, a + 1);
        for (int i = 1, u, v; i < n; ++i)
            scanf("%d%d", &u, &v), A(u, v), A(v, u);
        scanf("%s", o + 1);
        for (int i = 1; i <= m; ++i)
            Z[i][0] = Z[i - 1][0] * 233 + o[m - i + 1], Z[i][1] = Z[i - 1][1] * 233 + o[i];
        R = 0;
        X(1, 0, n);
        Q(R, 0);
        printf("%llu\n", q);
        c = q = 0;
        for (int i = 1; i <= n; ++i)
            h[i] = b[i] = 0;
    }
    return 0;
}
posted @   Jijidawang  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示