#3486
Hard Algorithms Longest special path ii
Array Hash Table Tree Depth-First Search Prefix Sum
18.6% acceptance
Feb 25, 2026
33
6
You are given an undirected tree rooted at node 0, with n nodes numbered from 0 to n - 1. This is represented by a 2D array edges of length n - 1, where edges[i] = [ui, vi, lengthi] indicates an edge between nodes ui and vi with length lengthi. You are also given an integer array nums, where nums[i] represents the value at node i.
A special path is defined as a downward path from an ancestor node to a descendant node in which all node values are distinct, except for at most one value that may appear twice.
Return an array result of size 2, where result[0] is the length of the longest special path, and result[1] is the minimum number of nodes in all possible longest special paths.
Solution
Rust
Time O(n * m)
Space O(n * m)
impl Solution {
pub fn longest_special_path(edges: Vec<Vec<i32>>, nums: Vec<i32>) -> Vec<i32> {
let n = nums.len();
let mut adj: Vec<Vec<(usize, i64)>> = vec![vec![]; n];
for e in &edges {
let (u, v, w) = (e[0] as usize, e[1] as usize, e[2] as i64);
adj[u].push((v, w));
adj[v].push((u, w));
}
let mut best_len = 0i64;
let mut best_nodes = 1i32;
// prefix[d] = total edge weight from root (depth 0) down to the node at depth d.
// Maintained as a stack: pushed when descending, popped when backtracking.
let mut prefix = vec![0i64];
// occ[val] = sorted list of depths at which `val` appears on the current root→node path.
let mut occ: std::collections::HashMap<i32, Vec<usize>> = std::collections::HashMap::new();
// Invariant: before entering each node, the window [min_start, d-1] has at most 1 duplicate value.
// dup_val = -1 means no dup; otherwise it holds the one duplicated value.
let mut dup_val: i32 = -1;
let mut min_start: usize = 0;
fn dfs(
node: usize, parent: usize, depth: usize,
adj: &Vec<Vec<(usize, i64)>>, nums: &[i32],
prefix: &mut Vec<i64>,
occ: &mut std::collections::HashMap<i32, Vec<usize>>,
dup_val: &mut i32,
min_start: &mut usize,
best_len: &mut i64,
best_nodes: &mut i32,
) {
let val = nums[node];
let d = depth;
let saved_min = *min_start;
let saved_dup = *dup_val;
occ.entry(val).or_default().push(d);
// Compute how many times `val` appears in the current window [min_start, d].
// Also capture the "first in window" index and second-to-last occurrence for reuse.
let (win_occ, first_win_idx) = {
let o = occ.get(&val).unwrap();
let fwi = o.partition_point(|&x| x < *min_start);
(o.len() - fwi, fwi)
};
if win_occ >= 3 {
// `val` was already the single dup (invariant); it now appears 3 times.
// Advance min_start past its first in-window occurrence so it stays at exactly 2.
let first_in_win = occ.get(&val).unwrap()[first_win_idx];
*min_start = first_in_win + 1;
// dup_val stays = val (still 2 occurrences in new window)
} else if win_occ == 2 {
// `val` just became a duplicate.
// fv = the occurrence that was already in the window before this push = o[-2]
let fv = {
let o = occ.get(&val).unwrap();
o[o.len() - 2]
};
if *dup_val == -1 {
// No prior dup; val becomes the single dup.
*dup_val = val;
} else {
// There is already another dup `u`. Advance min_start to leave at most 1 dup.
// fu = u's first occurrence in the current window.
let u = *dup_val;
let fu = {
let ou = occ.get(&u).unwrap();
let fu_idx = ou.partition_point(|&x| x < *min_start);
ou[fu_idx]
};
if fu < fv {
// Advance past u's first; u loses its duplicate status, val keeps it.
*min_start = fu + 1;
*dup_val = val;
} else if fv < fu {
// Advance past val's first; val loses its duplicate status, u keeps it.
*min_start = fv + 1;
// dup_val stays = u
} else {
// fu == fv: both lose their first occurrence; neither remains a dup.
*min_start = fu + 1;
*dup_val = -1;
}
}
}
// Window is now valid. Compute length and node count.
let len = prefix[d] - prefix[*min_start];
let nodes = (d + 1 - *min_start) as i32;
if len > *best_len || (len == *best_len && nodes < *best_nodes) {
*best_len = len;
*best_nodes = nodes;
}
for &(nb, w) in &adj[node] {
if nb == parent { continue; }
prefix.push(prefix[d] + w);
dfs(nb, node, d + 1, adj, nums, prefix, occ, dup_val, min_start, best_len, best_nodes);
prefix.pop();
}
// Backtrack: restore state
occ.get_mut(&val).unwrap().pop();
*min_start = saved_min;
*dup_val = saved_dup;
}
dfs(0, usize::MAX, 0, &adj, &nums, &mut prefix, &mut occ, &mut dup_val, &mut min_start, &mut best_len, &mut best_nodes);
vec![best_len as i32, best_nodes]
}
}