Welcome to Subscribe On Youtube
3841. Palindromic Path Queries in a Tree
Description
You are given an undirected tree with n nodes labeled 0 to n - 1. This is represented by a 2D array edges of length n - 1, where edges[i] = [ui, vi] indicates an undirected edge between nodes ui and vi.
You are also given a string s of length n consisting of lowercase English letters, where s[i] represents the character assigned to node i.
You are also given a string array queries, where each queries[i] is either:
"update ui c": Change the character at nodeuitoc. Formally, updates[ui] = c."query ui vi": Determine whether the string formed by the characters on the unique path fromuitovi(inclusive) can be rearranged into a palindrome.
Return a boolean array answer, where answer[j] is true if the jth query of type "query ui vi" can be rearranged into a palindrome, and false otherwise.
Example 1:
Input: n = 3, edges = [[0,1],[1,2]], s = "aac", queries = ["query 0 2","update 1 b","query 0 2"]
Output: [true,false]
Explanation:
"query 0 2": Path0 → 1 → 2gives"aac", which can be rearranged to form"aca", a palindrome. Thus,answer[0] = true."update 1 b": Update node 1 to'b', nows = "abc"."query 0 2": Path characters are"abc", which cannot be rearranged to form a palindrome. Thus,answer[1] = false.
Thus, answer = [true, false].
Example 2:
Input: n = 4, edges = [[0,1],[0,2],[0,3]], s = "abca", queries = ["query 1 2","update 0 b","query 2 3","update 3 a","query 1 3"]
Output: [false,false,true]
Explanation:
"query 1 2": Path1 → 0 → 2gives"bac", which cannot be rearranged to form a palindrome. Thus,answer[0] = false."update 0 b": Update node 0 to'b', nows = "bbca"."query 2 3": Path2 → 0 → 3gives"cba", which cannot be rearranged to form a palindrome. Thus,answer[1] = false."update 3 a": Update node 3 to'a',s = "bbca"."query 1 3": Path1 → 0 → 3gives"bba", which can be rearranged to form"bab", a palindrome. Thus,answer[2] = true.
Thus, answer = [false, false, true].
Constraints:
1 <= n == s.length <= 5 * 104edges.length == n - 1edges[i] = [ui, vi]0 <= ui, vi <= n - 1sconsists of lowercase English letters.- The input is generated such that
edgesrepresents a valid tree. 1 <= queries.length <= 5 * 104queries[i] = "update ui c"orqueries[i] = "query ui vi"0 <= ui, vi <= n - 1cis a lowercase English letter.
Solutions
Solution 1
-
class BinaryIndexedTree { int n; int[] tree; BinaryIndexedTree(int n) { this.n = n; tree = new int[n + 1]; } void update(int i, int delta) { for (i++; i <= n; i += i & -i) { tree[i] ^= delta; } } int query(int i) { int res = 0; for (; i > 0; i -= i & -i) { res ^= tree[i]; } return res; } } class Solution { List<Integer>[] g; int[] parent, depth, tin, tout, bit; int[][] up; int timer, m; public List<Boolean> palindromePath(int n, int[][] edges, String s, String[] queries) { g = new List[n]; for (int i = 0; i < n; i++) { g[i] = new ArrayList<>(); } for (int[] e : edges) { g[e[0]].add(e[1]); g[e[1]].add(e[0]); } parent = new int[n]; depth = new int[n]; tin = new int[n]; tout = new int[n]; timer = 0; dfs(0, -1); m = 32 - Integer.numberOfLeadingZeros(n); up = new int[m][n]; System.arraycopy(parent, 0, up[0], 0, n); for (int j = 1; j < m; j++) { for (int i = 0; i < n; i++) { up[j][i] = up[j - 1][up[j - 1][i]]; } } bit = new int[n]; for (int i = 0; i < n; i++) { bit[i] = 1 << (s.charAt(i) - 'a'); } BinaryIndexedTree tree = new BinaryIndexedTree(n); for (int i = 0; i < n; i++) { tree.update(tin[i], bit[i]); tree.update(tout[i], bit[i]); } List<Boolean> ans = new ArrayList<>(); for (String q : queries) { String[] parts = q.split(" "); int u = Integer.parseInt(parts[1]); if (parts[0].equals("update")) { int x = bit[u]; int y = 1 << (parts[2].charAt(0) - 'a'); if (x != y) { int delta = x ^ y; tree.update(tin[u], delta); tree.update(tout[u], delta); bit[u] = y; } } else { int v = Integer.parseInt(parts[2]); int w = lca(u, v); int mask = tree.query(tin[u] + 1) ^ tree.query(tin[v] + 1) ^ bit[w]; ans.add((mask & (mask - 1)) == 0); } } return ans; } void dfs(int u, int p) { parent[u] = p == -1 ? 0 : p; tin[u] = timer++; for (int v : g[u]) { if (v != p) { depth[v] = depth[u] + 1; dfs(v, u); } } tout[u] = timer; } int lca(int u, int v) { if (depth[u] < depth[v]) { int t = u; u = v; v = t; } int d = depth[u] - depth[v]; for (int j = 0; d > 0; j++, d >>= 1) { if ((d & 1) != 0) { u = up[j][u]; } } if (u == v) { return u; } for (int j = m - 1; j >= 0; j--) { if (up[j][u] != up[j][v]) { u = up[j][u]; v = up[j][v]; } } return parent[u]; } } -
class BinaryIndexedTree { int n; vector<int> tree; public: BinaryIndexedTree(int n) : n(n) , tree(n + 1) {} void update(int i, int delta) { for (++i; i <= n; i += i & -i) { tree[i] ^= delta; } } int query(int i) { int res = 0; for (; i > 0; i -= i & -i) { res ^= tree[i]; } return res; } }; class Solution { vector<vector<int>> g, up; vector<int> parent, depth, tin, tout, bit; int timer, m; void dfs(int u, int p) { parent[u] = p == -1 ? 0 : p; tin[u] = timer++; for (int v : g[u]) { if (v != p) { depth[v] = depth[u] + 1; dfs(v, u); } } tout[u] = timer; } int lca(int u, int v) { if (depth[u] < depth[v]) { swap(u, v); } int d = depth[u] - depth[v]; for (int j = 0; d; j++, d >>= 1) { if (d & 1) { u = up[j][u]; } } if (u == v) { return u; } for (int j = m - 1; j >= 0; j--) { if (up[j][u] != up[j][v]) { u = up[j][u]; v = up[j][v]; } } return parent[u]; } public: vector<bool> palindromePath(int n, vector<vector<int>>& edges, string s, vector<string>& queries) { g.assign(n, {}); for (auto& e : edges) { g[e[0]].push_back(e[1]); g[e[1]].push_back(e[0]); } parent.resize(n); depth.assign(n, 0); tin.resize(n); tout.resize(n); timer = 0; dfs(0, -1); m = 32 - __builtin_clz(n); up.assign(m, vector<int>(n)); up[0] = parent; for (int j = 1; j < m; j++) { for (int i = 0; i < n; i++) { up[j][i] = up[j - 1][up[j - 1][i]]; } } bit.resize(n); for (int i = 0; i < n; i++) { bit[i] = 1 << (s[i] - 'a'); } BinaryIndexedTree tree(n); for (int i = 0; i < n; i++) { tree.update(tin[i], bit[i]); tree.update(tout[i], bit[i]); } vector<bool> ans; for (string& q : queries) { stringstream ss(q); string op; int u; ss >> op >> u; if (op == "update") { string c; ss >> c; int y = 1 << (c[0] - 'a'); if (bit[u] != y) { int delta = bit[u] ^ y; tree.update(tin[u], delta); tree.update(tout[u], delta); bit[u] = y; } } else { int v; ss >> v; int w = lca(u, v); int mask = tree.query(tin[u] + 1) ^ tree.query(tin[v] + 1) ^ bit[w]; ans.push_back((mask & (mask - 1)) == 0); } } return ans; } }; -
class BinaryIndexedTree: def __init__(self, n: int): self.n = n self.tree = [0] * (n + 1) def update(self, i: int, delta: int) -> None: i += 1 while i <= self.n: self.tree[i] ^= delta i += i & -i def query(self, i: int) -> int: res = 0 while i: res ^= self.tree[i] i -= i & -i return res class Solution: def palindromePath( self, n: int, edges: list[list[int]], s: str, queries: list[str] ) -> list[bool]: g = [[] for _ in range(n)] for u, v in edges: g[u].append(v) g[v].append(u) parent = [-1] * n depth = [0] * n tin = [0] * n tout = [0] * n timer = 0 def dfs(u: int, p: int) -> None: nonlocal timer parent[u] = p tin[u] = timer timer += 1 for v in g[u]: if v != p: depth[v] = depth[u] + 1 dfs(v, u) tout[u] = timer dfs(0, -1) m = n.bit_length() up = [parent[:]] up[0][0] = 0 for j in range(1, m): prev = up[-1] up.append([prev[prev[i]] for i in range(n)]) def lca(u: int, v: int) -> int: if depth[u] < depth[v]: u, v = v, u d = depth[u] - depth[v] j = 0 while d: if d & 1: u = up[j][u] d >>= 1 j += 1 if u == v: return u for j in range(m - 1, -1, -1): if up[j][u] != up[j][v]: u = up[j][u] v = up[j][v] return parent[u] bit = [1 << (ord(c) - ord("a")) for c in s] tree = BinaryIndexedTree(n) for u in range(n): tree.update(tin[u], bit[u]) tree.update(tout[u], bit[u]) ans = [] for q in queries: op, a, b = q.split() u = int(a) if op == "update": x = bit[u] y = 1 << (ord(b) - ord("a")) if x != y: delta = x ^ y tree.update(tin[u], delta) tree.update(tout[u], delta) bit[u] = y else: v = int(b) w = lca(u, v) mask = tree.query(tin[u] + 1) ^ tree.query(tin[v] + 1) ^ bit[w] ans.append(mask & (mask - 1) == 0) return ans -
type BinaryIndexedTree struct { n int tree []int } func NewBinaryIndexedTree(n int) *BinaryIndexedTree { return &BinaryIndexedTree{n: n, tree: make([]int, n+1)} } func (b *BinaryIndexedTree) update(i, delta int) { for i++; i <= b.n; i += i & -i { b.tree[i] ^= delta } } func (b *BinaryIndexedTree) query(i int) int { res := 0 for ; i > 0; i -= i & -i { res ^= b.tree[i] } return res } func palindromePath(n int, edges [][]int, s string, queries []string) []bool { g := make([][]int, n) for _, e := range edges { u, v := e[0], e[1] g[u] = append(g[u], v) g[v] = append(g[v], u) } parent := make([]int, n) depth := make([]int, n) tin := make([]int, n) tout := make([]int, n) timer := 0 var dfs func(int, int) dfs = func(u, p int) { if p == -1 { parent[u] = 0 } else { parent[u] = p } tin[u] = timer timer++ for _, v := range g[u] { if v != p { depth[v] = depth[u] + 1 dfs(v, u) } } tout[u] = timer } dfs(0, -1) m := 1 for (1 << m) <= n { m++ } up := make([][]int, m) up[0] = append([]int(nil), parent...) for j := 1; j < m; j++ { up[j] = make([]int, n) for i := 0; i < n; i++ { up[j][i] = up[j-1][up[j-1][i]] } } bit := make([]int, n) for i := 0; i < n; i++ { bit[i] = 1 << (s[i] - 'a') } tree := NewBinaryIndexedTree(n) for i := 0; i < n; i++ { tree.update(tin[i], bit[i]) tree.update(tout[i], bit[i]) } lca := func(u, v int) int { if depth[u] < depth[v] { u, v = v, u } d := depth[u] - depth[v] for j := 0; d > 0; j++ { if d&1 != 0 { u = up[j][u] } d >>= 1 } if u == v { return u } for j := m - 1; j >= 0; j-- { if up[j][u] != up[j][v] { u = up[j][u] v = up[j][v] } } return parent[u] } ans := make([]bool, 0) for _, q := range queries { var op string var u int fmt.Sscanf(q, "%s %d", &op, &u) if op == "update" { var c string fmt.Sscanf(q, "%s %d %s", &op, &u, &c) y := 1 << (c[0] - 'a') if bit[u] != y { delta := bit[u] ^ y tree.update(tin[u], delta) tree.update(tout[u], delta) bit[u] = y } } else { var v int fmt.Sscanf(q, "%s %d %d", &op, &u, &v) w := lca(u, v) mask := tree.query(tin[u]+1) ^ tree.query(tin[v]+1) ^ bit[w] ans = append(ans, mask&(mask-1) == 0) } } return ans } -
class BinaryIndexedTree { n: number; tree: number[]; constructor(n: number) { this.n = n; this.tree = Array(n + 1).fill(0); } update(i: number, delta: number): void { for (i++; i <= this.n; i += i & -i) { this.tree[i] ^= delta; } } query(i: number): number { let res = 0; for (; i > 0; i -= i & -i) { res ^= this.tree[i]; } return res; } } function palindromePath(n: number, edges: number[][], s: string, queries: string[]): boolean[] { const g: number[][] = Array.from({ length: n }, () => []); for (const [u, v] of edges) { g[u].push(v); g[v].push(u); } const parent = Array(n).fill(0); const depth = Array(n).fill(0); const tin = Array(n).fill(0); const tout = Array(n).fill(0); let timer = 0; function dfs(u: number, p: number): void { parent[u] = p === -1 ? 0 : p; tin[u] = timer++; for (const v of g[u]) { if (v !== p) { depth[v] = depth[u] + 1; dfs(v, u); } } tout[u] = timer; } dfs(0, -1); const m = 32 - Math.clz32(n); const up: number[][] = Array.from({ length: m }, () => Array(n).fill(0)); up[0] = [...parent]; for (let j = 1; j < m; j++) { for (let i = 0; i < n; i++) { up[j][i] = up[j - 1][up[j - 1][i]]; } } const bit = Array(n).fill(0); for (let i = 0; i < n; i++) { bit[i] = 1 << (s.charCodeAt(i) - 97); } const tree = new BinaryIndexedTree(n); for (let i = 0; i < n; i++) { tree.update(tin[i], bit[i]); tree.update(tout[i], bit[i]); } function lca(u: number, v: number): number { if (depth[u] < depth[v]) { [u, v] = [v, u]; } let d = depth[u] - depth[v]; for (let j = 0; d > 0; j++, d >>= 1) { if (d & 1) { u = up[j][u]; } } if (u === v) { return u; } for (let j = m - 1; j >= 0; j--) { if (up[j][u] !== up[j][v]) { u = up[j][u]; v = up[j][v]; } } return parent[u]; } const ans: boolean[] = []; for (const q of queries) { const parts = q.split(' '); const u = Number(parts[1]); if (parts[0] === 'update') { const y = 1 << (parts[2].charCodeAt(0) - 97); if (bit[u] !== y) { const delta = bit[u] ^ y; tree.update(tin[u], delta); tree.update(tout[u], delta); bit[u] = y; } } else { const v = Number(parts[2]); const w = lca(u, v); const mask = tree.query(tin[u] + 1) ^ tree.query(tin[v] + 1) ^ bit[w]; ans.push((mask & (mask - 1)) === 0); } } return ans; } -
struct BinaryIndexedTree { n: usize, tree: Vec<i32>, } impl BinaryIndexedTree { fn new(n: usize) -> Self { Self { n, tree: vec![0; n + 1], } } fn update(&mut self, mut i: usize, delta: i32) { i += 1; while i <= self.n { self.tree[i] ^= delta; i += i & i.wrapping_neg(); } } fn query(&self, mut i: usize) -> i32 { let mut res = 0; while i > 0 { res ^= self.tree[i]; i -= i & i.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![]; 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 mut parent = vec![0usize; n]; let mut depth = vec![0usize; n]; let mut tin = vec![0usize; n]; let mut tout = vec![0usize; n]; let mut timer = 0usize; fn dfs( u: usize, p: usize, g: &[Vec<usize>], parent: &mut [usize], depth: &mut [usize], tin: &mut [usize], tout: &mut [usize], timer: &mut usize, ) { parent[u] = if p == usize::MAX { 0 } else { p }; tin[u] = *timer; *timer += 1; for &v in &g[u] { if v != p { depth[v] = depth[u] + 1; dfs(v, u, g, parent, depth, tin, tout, timer); } } tout[u] = *timer; } dfs( 0, usize::MAX, &g, &mut parent, &mut depth, &mut tin, &mut tout, &mut timer, ); let m = (32 - (n as u32).leading_zeros()) as usize; let mut up = vec![vec![0usize; n]; m]; up[0].clone_from(&parent); for j in 1..m { for i in 0..n { up[j][i] = up[j - 1][up[j - 1][i]]; } } let lca = |mut u: usize, mut v: usize| -> usize { if depth[u] < depth[v] { std::mem::swap(&mut u, &mut v); } let mut d = depth[u] - depth[v]; let mut j = 0; while d > 0 { if d & 1 == 1 { u = up[j][u]; } d >>= 1; j += 1; } if u == v { return u; } for j in (0..m).rev() { if up[j][u] != up[j][v] { u = up[j][u]; v = up[j][v]; } } parent[u] }; let bytes = s.as_bytes(); let mut bit = vec![0i32; n]; for i in 0..n { bit[i] = 1 << (bytes[i] - b'a') as u32; } let mut tree = BinaryIndexedTree::new(n); for i in 0..n { tree.update(tin[i], bit[i]); tree.update(tout[i], bit[i]); } let mut ans = Vec::new(); for q in &queries { let parts: Vec<&str> = q.split(' ').collect(); let u: usize = parts[1].parse().unwrap(); if parts[0] == "update" { let x = bit[u]; let y = 1 << (parts[2].as_bytes()[0] - b'a') as u32; if x != y { let delta = x ^ y; tree.update(tin[u], delta); tree.update(tout[u], delta); bit[u] = y; } } else { let v: usize = parts[2].parse().unwrap(); let w = lca(u, v); let mask = tree.query(tin[u] + 1) ^ tree.query(tin[v] + 1) ^ bit[w]; ans.push(mask & (mask - 1) == 0); } } ans } }