Count Pairs of Connectable Servers in a Weighted Tree Network
MedPrompt
You are given an unrooted weighted tree with n vertices representing servers numbered from 0 to n - 1, an array edges where edges[i] = [ai, bi, weighti] represents a bidirectional edge between vertices ai and bi of weight weighti. You are also given an integer signalSpeed.
Two servers a and b are connectable through a server c if:
a < b,a != candb != c.- The distance from
ctoais divisible bysignalSpeed. - The distance from
ctobis divisible bysignalSpeed. - The path from
ctoband the path fromctoado not share any edges.
Return an integer array count of length n where count[i] is the number of server pairs that are connectable through the server i.
Example 1:
Input: edges = [[0,1,1],[1,2,5],[2,3,13],[3,4,9],[4,5,2]], signalSpeed = 1
Output: [0,4,6,6,4,0]
Explanation: Since signalSpeed is 1, count[c] is equal to the number of pairs of paths that start at c and do not share any edges.
In the case of the given path graph, count[c] is equal to the number of servers to the left of c multiplied by the servers to the right of c.Example 2:
Input: edges = [[0,6,3],[6,5,3],[0,3,1],[3,2,7],[3,1,6],[3,4,2]], signalSpeed = 3
Output: [2,0,0,0,0,0,2]
Explanation: Through server 0, there are 2 pairs of connectable servers: (4, 5) and (4, 6).
Through server 6, there are 2 pairs of connectable servers: (4, 5) and (0, 5).
It can be shown that no two servers are connectable through servers other than 0 and 6.
Constraints:
2 <= n <= 1000edges.length == n - 1edges[i].length == 30 <= ai, bi < nedges[i] = [ai, bi, weighti]1 <= weighti <= 1061 <= signalSpeed <= 106- The input is generated such that
edgesrepresents a valid tree.
Approaches
2 approaches with complexity analysis and trade-offs.
This is a direct, brute-force approach. For each server in the network, we consider it as the central server c. Then, we examine every possible pair of other servers (a, b) to see if they are connectable through c.
Algorithm
- Build an adjacency list from the
edgesarray. - Initialize an answer array
resultof sizen. - For each server
cfrom0ton-1:- Perform a graph traversal (like BFS) starting from
cto calculate distances and the first neighbor on the path to every other node. Store these indistandfirstHoparrays. - Initialize
pairCount = 0. - Iterate
afrom0ton-1. - Iterate
bfroma + 1ton-1. - If
a == corb == c, skip this pair. - Check if
dist[a] % signalSpeed == 0,dist[b] % signalSpeed == 0, andfirstHop[a] != firstHop[b]. - If all true, increment
pairCount. - Set
result[c] = pairCount.
- Perform a graph traversal (like BFS) starting from
- Return
result.
Walkthrough
To check if a pair (a, b) is connectable through c, we must verify all four conditions given in the problem. The main computational tasks are finding the distances from c to a and b, and ensuring their paths from c are edge-disjoint.
A single traversal like Breadth-First Search (BFS) or Depth-First Search (DFS) starting from c can efficiently compute the distances to all other nodes in O(N) time. During this same traversal, we can also determine the "first hop" for each node, which is the neighbor of c on the unique path from c to that node. The path disjointness condition is met if and only if a and b have different first hops from c.
The overall algorithm is as follows:
- Build an adjacency list representation of the tree.
- For each server
cfrom0ton-1: a. Run a traversal (e.g., BFS) fromcto computedistances[i]andfirst_hops[i]for all other nodesi. b. Initialize a counter for connectable pairs to zero. c. Iterate through all pairs of servers(a, b)witha < b. d. Ifaorbis the same asc, skip. e. Check ifdistances[a]anddistances[b]are divisible bysignalSpeed, and iffirst_hops[a]is different fromfirst_hops[b]. f. If all conditions hold, increment the pair counter. g. After checking all pairs, store the result for serverc. - Return the final counts.
Complexity
Time
O(N^3). The main loop runs `N` times. Inside, the traversal takes O(N). The nested loops to check all pairs `(a, b)` take O(N^2). Thus, the total complexity is O(N * (N + N^2)) = O(N^3), which is too slow for the given constraints.
Space
O(N) to store the adjacency list, a distance array, and a first-hop array for each central server.
Trade-offs
Pros
Conceptually simple and directly follows the problem definition.
Cons
The O(N^3) time complexity makes it infeasible for the given constraints.
This approach improves upon the brute-force method by changing the way we count pairs. Instead of checking every pair (a, b), for each potential central server c, we first count how many valid nodes exist in each of its branches. A "branch" corresponds to a subtree attached to one of c's neighbors. If a branch i has count_i valid nodes, and another branch j has count_j valid nodes, they contribute count_i * count_j pairs to the total.
Algorithm
- Build an adjacency list to represent the tree.
- Initialize an answer array
resultof sizenwith zeros. - Loop for
ifrom0ton-1(treatingias the central serverc):- If
ihas fewer than two neighbors, it cannot connect any pair, soresult[i]remains0. Continue. - Initialize
totalPairs = 0andprefixCount = 0. - For each neighbor
vofi:- Start a traversal (e.g., DFS) from
v, withias the parent to avoid going back. - The initial distance for the traversal is the weight of the edge
(i, v). - The traversal counts nodes
xin the subtree wheredistance(i, x)is divisible bysignalSpeed. Let this becountInSubtree. - Add
countInSubtree * prefixCounttototalPairs. - Update
prefixCountby addingcountInSubtree.
- Start a traversal (e.g., DFS) from
- Set
result[i] = totalPairs.
- If
- Return
result.
Walkthrough
The algorithm iterates through each server i and considers it as the central server c. For c, the paths to any two nodes a and b are edge-disjoint if and only if a and b lie in subtrees attached to different neighbors of c.
The process for each c is as follows:
- For each neighbor
vofc, traverse the subtree rooted atv(while not going back toc). - During this traversal, count how many nodes
xsatisfy the conditiondistance(c, x) % signalSpeed == 0. Note thatdistance(c, x) = weight(c, v) + distance(v, x). - Let's say the counts for the branches are
c_1, c_2, ..., c_k. The total number of pairs is the sum ofc_i * c_jfor alli < j. This can be computed efficiently in a single pass over the counts. We maintain aprefix_sumof counts seen so far. For the current countc_i, we addc_i * prefix_sumto the total result forc, and then updateprefix_sumby addingc_i.
Here is a Java implementation of this approach:
import java.util.ArrayList;import java.util.List; class Solution { public int[] countPairsOfConnectableServers(int[][] edges, int signalSpeed) { int n = edges.length + 1; List<int[]>[] adj = new ArrayList[n]; for (int i = 0; i < n; i++) { adj[i] = new ArrayList<>(); } for (int[] edge : edges) { adj[edge[0]].add(new int[]{edge[1], edge[2]}); adj[edge[1]].add(new int[]{edge[0], edge[2]}); } int[] result = new int[n]; for (int i = 0; i < n; i++) { if (adj[i].size() < 2) { continue; } int totalPairs = 0; int prefixCount = 0; for (int[] neighborInfo : adj[i]) { int neighbor = neighborInfo[0]; int weight = neighborInfo[1]; int countInSubtree = dfs(neighbor, i, weight, signalSpeed, adj); totalPairs += countInSubtree * prefixCount; prefixCount += countInSubtree; } result[i] = totalPairs; } return result; } private int dfs(int u, int p, int currentDist, int signalSpeed, List<int[]>[] adj) { int count = 0; if (currentDist % signalSpeed == 0) { count++; } for (int[] neighborInfo : adj[u]) { int v = neighborInfo[0]; int w = neighborInfo[1]; if (v != p) { count += dfs(v, u, currentDist + w, signalSpeed, adj); } } return count; }}Complexity
Time
O(N^2). The outer loop runs `N` times. For each server `i`, we perform a traversal for each of its neighbors. The total nodes visited across all these traversals for a single `i` is `N-1`. So, the work for each `i` is O(N). The total time complexity is O(N*N).
Space
O(N). This is for the adjacency list and the recursion stack for DFS. In the worst case (a path graph), the recursion depth can be O(N).
Trade-offs
Pros
Efficient enough for the given constraints (N <= 1000).
Relatively straightforward to implement compared to more advanced tree algorithms.
Cons
The quadratic time complexity might be too slow if N were significantly larger.
Solutions
Solution
class Solution {private int signalSpeed;private List<int[]>[] g;public int[] countPairsOfConnectableServers(int[][] edges, int signalSpeed) { int n = edges.length + 1; g = new List[n]; this.signalSpeed = signalSpeed; Arrays.setAll(g, k->new ArrayList<>()); for (var e : edges) { int a = e[0], b = e[1], w = e[2]; g[a].add(new int[]{b, w}); g[b].add(new int[]{a, w}); } int[] ans = new int[n]; for (int a = 0; a < n; ++a) { int s = 0; for (var e : g[a]) { int b = e[0], w = e[1]; int t = dfs(b, a, w); ans[a] += s * t; s += t; } } return ans; }private int dfs(int a, int fa, int ws) { int cnt = ws % signalSpeed == 0 ? 1 : 0; for (var e : g[a]) { int b = e[0], w = e[1]; if (b != fa) { cnt += dfs(b, a, ws + w); } } return cnt; }}class Solution {public: vector<int> countPairsOfConnectableServers(vector<vector<int>> &edges, int signalSpeed) { int n = edges.size() + 1; vector<pair<int, int>> g[n]; for (auto &e : edges) { int a = e[0], b = e[1], w = e[2]; g[a].emplace_back(b, w); g[b].emplace_back(a, w); } function<int(int, int, int)> dfs = [&](int a, int fa, int ws) { int cnt = ws % signalSpeed == 0; for (auto &[b, w] : g[a]) { if (b != fa) { cnt += dfs(b, a, ws + w); } } return cnt; }; vector<int> ans(n); for (int a = 0; a < n; ++a) { int s = 0; for (auto &[b, w] : g[a]) { int t = dfs(b, a, w); ans[a] += s * t; s += t; } } return ans; }};class Solution: def countPairsOfConnectableServers(self, edges: List[List[int]], signalSpeed: int) -> List[int]: def dfs(a: int, fa: int, ws: int) -> int: cnt = 0 if ws % signalSpeed else 1 for b, w in g[a]: if b != fa: cnt += dfs(b, a, ws + w) return cnt n = len(edges) + 1 g = [[] for _ in range(n)] for a, b, w in edges: g[a]. append((b, w)) g[b]. append((a, w)) ans = [0] * n for a in range(n): s = 0 for b, w in g[a]: t = dfs(b, a, w) ans[a] += s * t s += t return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.