[BZOJ4598][Sdoi2016]模式字符串(点分治+Hash)

这种题啃腚是用能解决一切树上路径问题的点分治。
考虑当前分治中心为 u u 时怎样处理子树内经过 u 的合法路径数量。
u u 开始进行一遍 dfs ,
处理出对于 u 的子树内每个点 v v v>u 的 hash 值和 u>v u − > v 的 hash 值。
然后对于每条路径 v>u v − > u (记长度为 len l e n lenmodm=k l e n mod m = k ),
如果长度为 lenk l e n − k 的前缀不是模式串的重复串,或者长度为 k k 的后缀不是模式串的前缀,那么 v>u 这条路径对于 u u 是没有用的。
对于 u>v 也差不多。
然后用一个计数器,统计下 lenmodm l e n mod m 为从 0 0 m1 u>v u − > v v>u v − > u 路径数目,然后乱搞即可。
(注意要去除 u u v 来自同一子树时带来的多余路径)
代码:

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值