解题思路

核心在于快速判断树中任意两点路径上的字符能否重排为回文串。

· 回文判定:一个字符串能重排成回文,当且仅当其出现奇数次的字符最多只有一个。
    用 26 位整数(掩码)表示每个字符的奇偶性:第 i 位为 1 表示字符 i 出现奇数次。
· 前缀异或(XOR)
    定义 pref[x] 为从根节点到 x 的路径上所有字符的奇偶掩码。
    则 u 到 v 路径的掩码为:
    mask(u→v) = pref[u] ^ pref[v] ^ (1 << char(LCA(u,v)))。
· 动态更新
    修改节点 u 的字符时,会影响以 u 为根的整棵子树中所有节点的 pref 值。
    利用 DFS 序(欧拉序) 将子树映射为连续区间 [tin[u], tout[u]],再用 树状数组(Fenwick Tree) 维护区间异或更新和单点查询。
· LCA 查询:使用二进制提升(Binary Lifting)预处理,O(log n) 回答。

---

Rust 实现

```rust
struct BIT {
    bit: Vec<u32>,
    n: usize,
}

impl BIT {
    fn new(n: usize) -> Self {
        BIT {
            bit: vec![0; n + 2],
            n,
        }
    }

    fn add(&mut self, mut idx: usize, val: u32) {
        while idx <= self.n {
            self.bit[idx] ^= val;
            idx += idx & idx.wrapping_neg();  // lowbit
        }
    }

    // 区间 [l, r] 异或 val
    fn range_xor(&mut self, l: usize, r: usize, val: u32) {
        self.add(l, val);
        self.add(r + 1, val);
    }

    // 单点查询
    fn query(&self, mut idx: usize) -> u32 {
        let mut res = 0;
        while idx > 0 {
            res ^= self.bit[idx];
            idx -= idx & idx.wrapping_neg();
        }
        res
    }
}

impl Solution {
    pub fn palindrome_path(n: i32, edges: Vec<Vec<i32>>, s: String, queries: Vec<String>) -> Vec<bool> {
        let n = n as usize;
        // 建图
        let mut g = vec![Vec::new(); n];
        for e in edges {
            let u = e[0] as usize;
            let v = e[1] as usize;
            g[u].push(v);
            g[v].push(u);
        }

        let s_bytes = s.as_bytes();
        let mut depth = vec![0; n];
        let mut tin = vec![0; n];
        let mut tout = vec![0; n];
        let mut pref = vec![0u32; n];
        let mut parent = vec![vec![0; n]; 1]; // 第一层,后续扩展

        // ---------- 迭代 DFS 计算 tin, tout, depth, parent[0], pref ----------
        let mut stack = Vec::new();
        stack.push((0, -1, 0)); // (节点, 父节点, 状态) 状态0=进入, 1=离开
        let mut timer = 0;
        while let Some((u, p, state)) = stack.pop() {
            if state == 0 {
                timer += 1;
                tin[u] = timer;
                if p == -1 {
                    depth[u] = 0;
                    parent[0][u] = 0;
                    pref[u] = 1u32 << (s_bytes[u] - b'a');
                } else {
                    let p = p as usize;
                    depth[u] = depth[p] + 1;
                    parent[0][u] = p;
                    pref[u] = pref[p] ^ (1u32 << (s_bytes[u] - b'a'));
                }
                stack.push((u, p, 1)); // 退出标记
                for &v in &g[u] {
                    if p == -1 || v != p as usize {
                        stack.push((v, u as i32, 0));
                    }
                }
            } else {
                tout[u] = timer;
            }
        }

        // ---------- 二进制提升表 ----------
        let mut LOG = 1;
        while (1 << LOG) <= n {
            LOG += 1;
        }
        parent.resize(LOG, vec![0; n]);
        for k in 1..LOG {
            for i in 0..n {
                parent[k][i] = parent[k - 1][parent[k - 1][i]];
            }
        }

        // LCA 闭包
        let lca = |mut u: usize, mut v: usize| -> usize {
            if depth[u] < depth[v] {
                std::mem::swap(&mut u, &mut v);
            }
            let diff = depth[u] - depth[v];
            for k in 0..LOG {
                if diff & (1 << k) != 0 {
                    u = parent[k][u];
                }
            }
            if u == v {
                return u;
            }
            for k in (0..LOG).rev() {
                if parent[k][u] != parent[k][v] {
                    u = parent[k][u];
                    v = parent[k][v];
                }
            }
            parent[0][u]
        };

        let mut bit = BIT::new(n);
        let mut chars: Vec<u8> = s_bytes.iter().map(|&b| b - b'a').collect();
        let mut ans = Vec::new();

        for q in queries {
            let parts: Vec<&str> = q.split_whitespace().collect();
            match parts[0] {
                "update" => {
                    let u = parts[1].parse::<usize>().unwrap();
                    let c = parts[2].as_bytes()[0] - b'a';
                    if c != chars[u] {
                        let diff = (1u32 << chars[u]) ^ (1u32 << c);
                        bit.range_xor(tin[u], tout[u], diff);
                        chars[u] = c;
                    }
                }
                "query" => {
                    let u = parts[1].parse::<usize>().unwrap();
                    let v = parts[2].parse::<usize>().unwrap();
                    let w = lca(u, v);
                    let cur_u = pref[u] ^ bit.query(tin[u]);
                    let cur_v = pref[v] ^ bit.query(tin[v]);
                    let mask = cur_u ^ cur_v ^ (1u32 << chars[w]);
                    // 判断 mask 是否只有 0 或 1 个 1
                    ans.push((mask & (mask - 1)) == 0);
                }
                _ => {}
            }
        }
        ans
    }
}
```

---

复杂度分析

· 预处理:DFS 和二进制提升均 O(n log n)
· 每次查询/更新:O(log n)(LCA + 树状数组操作)
· 空间:O(n log n)(LCA 表)+ O(n)(其他数组)

该实现充分利用了位运算和区间数据结构,能够高效处理动态树上的回文路径查询。

 

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐