Graph Connectivity With Threshold

Hard
#1495Time: O(n^3 + Q * (n + E)), where `n` is the number of cities, `Q` is the number of queries, and `E` is the number of edges. In the worst case, `E` can be `O(n^2)`. The graph construction takes `O(n^3)` and each of the `Q` queries takes `O(n+E)`. This is prohibitively slow.Space: O(n + E), where E is the number of edges. In the worst case, E can be O(n^2), so the space complexity is O(n^2).
Algorithms
Data structures

Prompt

We have n cities labeled from 1 to n. Two different cities with labels x and y are directly connected by a bidirectional road if and only if x and y share a common divisor strictly greater than some threshold. More formally, cities with labels x and y have a road between them if there exists an integer z such that all of the following are true:

  • x % z == 0,
  • y % z == 0, and
  • z > threshold.

Given the two integers, n and threshold, and an array of queries, you must determine for each queries[i] = [ai, bi] if cities ai and bi are connected directly or indirectly. (i.e. there is some path between them).

Return an array answer, where answer.length == queries.length and answer[i] is true if for the ith query, there is a path between ai and bi, or answer[i] is false if there is no path.

 

Example 1:

Input: n = 6, threshold = 2, queries = [[1,4],[2,5],[3,6]]
Output: [false,false,true]
Explanation: The divisors for each number:
1:   1
2:   1, 2
3:   1, 3
4:   1, 2, 4
5:   1, 5
6:   1, 2, 3, 6
Using the underlined divisors above the threshold, only cities 3 and 6 share a common divisor, so they are the
only ones directly connected. The result of each query:
[1,4]   1 is not connected to 4
[2,5]   2 is not connected to 5
[3,6]   3 is connected to 6 through path 3--6

Example 2:

Input: n = 6, threshold = 0, queries = [[4,5],[3,4],[3,2],[2,6],[1,3]]
Output: [true,true,true,true,true]
Explanation: The divisors for each number are the same as the previous example. However, since the threshold is 0,
all divisors can be used. Since all numbers share 1 as a divisor, all cities are connected.

Example 3:

Input: n = 5, threshold = 1, queries = [[4,5],[4,5],[3,2],[2,3],[3,4]]
Output: [false,false,false,false,false]
Explanation: Only cities 2 and 4 share a common divisor 2 which is strictly greater than the threshold 1, so they are the only ones directly connected.
Please notice that there can be multiple queries for the same pair of nodes [x, y], and that the query [x, y] is equivalent to the query [y, x].

 

Constraints:

  • 2 <= n <= 104
  • 0 <= threshold <= n
  • 1 <= queries.length <= 105
  • queries[i].length == 2
  • 1 <= ai, bi <= cities
  • ai != bi

Approaches

3 approaches with complexity analysis and trade-offs.

This is the most naive approach. First, an explicit graph is constructed by checking every pair of cities for a direct connection. An adjacency list is used to store the graph. Then, for each query, a graph traversal algorithm like Breadth-First Search (BFS) or Depth-First Search (DFS) is run to check for a path between the two cities.

Algorithm

  • 1. Create an adjacency list adjof sizen+1 to represent the graph.
  • 2. For each integer ifrom1ton:
  • a. For each integerjfromi + 1ton:
  • i. Iteratezfromthreshold + 1toi.
  • ii. Ifi % z == 0andj % z == 0, an edge exists. Add jtoadj[i]anditoadj[j], then break the inner loop.
  • 3. Initialize an empty list answer to store query results.
  • 4. For each query [a, b]inqueries:
  • a. Perform a Breadth-First Search (BFS) or Depth-First Search (DFS) starting fromato check ifb is reachable.
  • b. Addtruetoanswerif a path is found, otherwise addfalse.
  • 5. Return the answer list.

Walkthrough

This method separates the problem into two distinct phases: graph building and path finding.

Graph Building:

  1. Create an adjacency list, adj, to represent the graph, where adj[i] stores the neighbors of city i.
  2. Iterate through all pairs of cities (i, j) with 1 <= i < j <= n.
  3. For each pair, check if they share a common divisor z > threshold. This can be done by iterating z from threshold + 1 to i.
  4. If such a z is found, add an edge between i and j in the adjacency list (i.e., add j to adj[i] and i to adj[j]).

Query Processing:

  1. For each query [a, b], perform a traversal (e.g., BFS) starting from city a.
  2. Use a visited array to keep track of visited nodes during the traversal.
  3. If city b is reached during the traversal, the cities are connected.
  4. If the traversal completes without reaching b, they are not connected.

This approach is highly inefficient because it rebuilds the path-finding work for every single query.

import java.util.*; class Solution {    public List<Boolean> areConnected(int n, int threshold, int[][] queries) {        // 1. Graph Building        List<List<Integer>> adj = new ArrayList<>();        for (int i = 0; i <= n; i++) {            adj.add(new ArrayList<>());        }         if (threshold == 0) {            List<Boolean> allConnected = new ArrayList<>();            for (int i = 0; i < queries.length; i++) allConnected.add(true);            return allConnected;        }         for (int i = 1; i <= n; i++) {            for (int j = i + 1; j <= n; j++) {                for (int z = threshold + 1; z <= i; z++) {                    if (i % z == 0 && j % z == 0) {                        adj.get(i).add(j);                        adj.get(j).add(i);                        break;                    }                }            }        }         // 2. Query Processing        List<Boolean> result = new ArrayList<>();        for (int[] query : queries) {            result.add(hasPath(query[0], query[1], n, adj));        }        return result;    }     private boolean hasPath(int start, int end, int n, List<List<Integer>> adj) {        if (start == end) return true;        Queue<Integer> queue = new LinkedList<>();        boolean[] visited = new boolean[n + 1];         queue.offer(start);        visited[start] = true;         while (!queue.isEmpty()) {            int curr = queue.poll();            if (curr == end) return true;             for (int neighbor : adj.get(curr)) {                if (!visited[neighbor]) {                    visited[neighbor] = true;                    queue.offer(neighbor);                }            }        }        return false;    }}

Complexity

Time

O(n^3 + Q * (n + E)), where `n` is the number of cities, `Q` is the number of queries, and `E` is the number of edges. In the worst case, `E` can be `O(n^2)`. The graph construction takes `O(n^3)` and each of the `Q` queries takes `O(n+E)`. This is prohibitively slow.

Space

O(n + E), where E is the number of edges. In the worst case, E can be O(n^2), so the space complexity is O(n^2).

Trade-offs

Pros

  • Very straightforward and easy to understand for those familiar with basic graph algorithms.

Cons

  • Extremely inefficient due to the O(n^3) graph construction time.

  • Query processing is also slow, as it repeats traversal work for each query.

  • The space complexity for the adjacency list can be large, up to O(n^2).

  • Guaranteed to result in Time Limit Exceeded for the given constraints.

Solutions

class UnionFind { private int [] p ; private int [] size ; public UnionFind ( int n ) { p = new int [ n ]; size = new int [ n ]; for ( int i = 0 ; i < n ; ++ i ) { p [ i ] = i ; size [ i ] = 1 ; } } public int find ( int x ) { if ( p [ x ] != x ) { p [ x ] = find ( p [ x ]); } return p [ x ]; } public boolean union ( int a , int b ) { int pa = find ( a ), pb = find ( b ); if ( pa == pb ) { return false ; } if ( size [ pa ] > size [ pb ]) { p [ pb ] = pa ; size [ pa ] += size [ pb ]; } else { p [ pa ] = pb ; size [ pb ] += size [ pa ]; } return true ; } } class Solution { public List < Boolean > areConnected ( int n , int threshold , int [][] queries ) { UnionFind uf = new UnionFind ( n + 1 ); for ( int a = threshold + 1 ; a <= n ; ++ a ) { for ( int b = a + a ; b <= n ; b += a ) { uf . union ( a , b ); } } List < Boolean > ans = new ArrayList <>(); for ( var q : queries ) { ans . add ( uf . find ( q [ 0 ]) == uf . find ( q [ 1 ])); } return ans ; } }

Video 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.