Bingbong的回文路径

Bingbong的回文路径

题目描述

Bingbong 有一棵有根树,根节点为 $1$,总共有 $n$ 个节点。树中的节点通过 $n−1$ 条无向边相连,每条边的权重为 $1$。

树中的每个节点包含一个小写字母。Bingbong 将选择从节点 $u$ 开始,并在选择最短路径的情况下到达节点 $v$。他想知道他所走路径形成的字符串是否是一个回文串。由于他会进行多次行走,您需要回答多个查询。

输入描述:

第一行包含一个整数 $n(1 \leq n \leq 10^5)$,表示节点的数量。

第二行包含 $n$ 个小写字母,其中第 $i$ 个字母表示第 $i$ 个节点上的小写字母。

第三行包含 $n$ 个整数,其中第 $i$ 个数字 $a_i​(1 \leq a_​i \leq i−1)$ 表示第 $i$ 个节点的父节点,对于根节点 $a_i​=0$。

第四行包含一个整数 $q(1 \leq q \leq 10^5)$,表示查询的数量。

接下来是 $q$ 行,每行包含两个整数 $u$ 和 $v$,表示一个查询的起点和终点。

输出描述:

输出 $q$ 行,每行包含一个字符串。如果查询的起点到终点的路径连接形成一个回文串,则输出 "YES",否则输出 "NO"。

示例1

输入

5
bbaaa
0 1 1 2 2
3
2 3
4 3
5 3

输出

NO
YES
YES

 

解题思路

  假设 $u$ 和 $v$ 的最近公共祖先是 $p$,从 $u$ 走到 $p$ 再走到 $v$ 的字符串表示为 $s_{upv}$,同理从 $v$ 走到 $p$ 再走到 $u$ 的字符串表示为 $s_{vpu}$。当 $s_{upv} = s_{vpu}$ 时说明 $u$ 到 $v$ 路径上的字符串是回文串。判断回文串可以用字符串哈希(这里基数 $P$ 取 $13331$,自然溢出取模),由于是树上的问题我们可以通过倍增来维护树链的信息。

  定义 $\mathrm{fa}[u][i]$ 表示从 $u$ 往上走 $2^i$ 步后达到的节点,$h_1[u][i]$ 表示从 $u$ 往上走 $2^i$ 步构成字符串的哈希值(不含 $u$ 上的字符),$h_2[u][i]$ 表示从 $\mathrm{fa}[u][i]$ 往下走 $2^i$ 步构成字符串的哈希值(含 $\mathrm{fa}[u][i]$ 上的字符)。以下图为例:

  从 $u$ 往上走 $2^2$ 步构成的字符串为 acbc,从 $\mathrm{fa}[u][2]$ 往下走 $2^2$ 步构成的字符串为 cbca

  考虑如何维护这些信息。对于 $\mathrm{fa}[u][i]$,我们先从 $u$ 往上走 $2^{i-1}$ 步到达 $\mathrm{fa}[u][i-1]$,再从 $\mathrm{fa}[u][i-1]$ 往上走 $2^{i-1}$ 步走到 $\mathrm{fa}[u][i]$,因此有 $\mathrm{fa}[u][i] = \mathrm{fa}[\mathrm{fa}[u][i-1]][i-1]$。

  对于两个字符串 $s_1$ 和 $s_2$,哈希值分别为 $h_1$ 和 $h_2$,那么拼接后的 $s_1 \, s_2$ 的哈希值就是 $h_1 \cdot P^{|s_2|} + h_2$。因此 $h_1[u][i] = h_1[u][i-1] \cdot P^{2^{i-1}} + h_1[\mathrm{fa}[u][i-1]][i-1]$,$h_2[u][i] = h_2[\mathrm{fa}[u][i-1]][i-1] \cdot P^{2^{i-1}} + h_2[u][i-1]$。

  对于询问的 $(u,v)$,我们可以仿照求最近公共祖先的做法,分别求出 $u \to v$ 和 $v \to u$ 对应字符串的哈希值。在 $u$ 往上跳到 $p$ 的过程中,维护 $hu_1$ 表示从 $u$ 往上跳的过程中构成字符串的哈希值,和 $hu_2$ 表示往下跳到 $u$ 的过程中构成字符串的哈希值。同理维护 $v$ 的 $hv_1$ 和 $hv_2$。最后让 $u$ 跳到 $p$,$v$ 跳到 $p$ 的下一个节点,判断是否满足 $hu_1 \cdot P^{d2} + hv_2 = hv_1 \cdot P^{d1} + hu_2$ 即可,其中 $d_1$ 表示 $u$ 往上跳经过的节点个数,$d_2$ 表示 $v$ 往上跳经过的节点个数。

  AC 代码如下,时间复杂度为 $O(n \log{n} + q \log{n})$:

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ULL;

const int N = 1e5 + 5, M = N * 2, P = 13331;

char s[N];
int h[N], e[M], ne[M], idx;
int fa[N][17], dep[N];
ULL h1[N][17], h2[N][17], pp[N];

void add(int u, int v) {
    e[idx] = v, ne[idx] = h[u], h[u] = idx++;
}

void dfs(int u, int p) {
    dep[u] = dep[p] + 1;
    fa[u][0] = p;
    h1[u][0] = h2[u][0] = s[p];
    for (int i = 1; i <= 16; i++) {
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
        h1[u][i] = h1[u][i - 1] * pp[1 << i - 1] + h1[fa[u][i - 1]][i - 1];
        h2[u][i] = h2[fa[u][i - 1]][i - 1] * pp[1 << i - 1] + h2[u][i - 1];
    }
    for (int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i];
        if (v == p) continue;
        dfs(v, u);
    }
}

bool query(int a, int b) {
    if (dep[a] < dep[b]) swap(a, b);
    ULL ha1 = s[a], ha2 = s[a], hb1 = s[b], hb2 = s[b];
    int d1 = 1, d2 = 1;
    for (int i = 16; i >= 0; i--) {
        if (dep[fa[a][i]] >= dep[b]) {
            ha1 = ha1 * pp[1 << i] + h1[a][i];
            ha2 = h2[a][i] * pp[d1] + ha2;
            d1 += 1 << i;
            a = fa[a][i];
        }
    }
    if (a == b) return ha1 == ha2;
    for (int i = 16; i >= 0; i--) {
        if (fa[a][i] != fa[b][i]) {
            ha1 = ha1 * pp[1 << i] + h1[a][i];
            ha2 = h2[a][i] * pp[d1] + ha2;
            d1 += 1 << i;
            a = fa[a][i];
            hb1 = hb1 * pp[1 << i] + h1[b][i];
            hb2 = h2[b][i] * pp[d2] + hb2;
            d2 += 1 << i;
            b = fa[b][i];
        }
    }
    // 当前a和b往上跳1步就会同时到达最近公共祖先,选择让a跳
    ha1 = ha1 * pp[1] + h1[a][0];
    ha2 = h2[a][0] * pp[d1++] + ha2;
    return ha1 * pp[d2] + hb2 == hb1 * pp[d1] + ha2;
}

int main() {
    int n, m;
    scanf("%d %s", &n, s + 1);
    memset(h, -1, sizeof(h));
    for (int i = 1; i <= n; i++) {
        int x;
        scanf("%d", &x);
        add(i, x), add(x, i);
    }
    pp[0] = 1;
    for (int i = 1; i <= n; i++) {
        pp[i] = pp[i - 1] * P;
    }
    dfs(1, 0);
    scanf("%d", &m);
    while (m--) {
        int a, b;
        scanf("%d %d", &a, &b);
        printf("%s\n", query(a, b) ? "YES" : "NO");
    }
    
    return 0;
}

 

参考资料

  牛客小白月赛91文字版题解:https://ac.nowcoder.com/discuss/1294108

posted @ 2024-04-20 16:15  onlyblues  阅读(30)  评论(0编辑  收藏  举报
Web Analytics