Given two integers N and K, where N denotes the number of vertices in a Graph. There is an edge between two vertices u and v if there exists some z > K: u % z ==0: v % z ==0. Now you have to answer Q queries of the form [ i, j ] where you have to print whether i and j are connected (i.e. there is some path between i and j ) or not.
Examples:
Input: N=6, K=2, queries: [[1, 4],[2, 5],[3, 6]]
Output: false false true
Explanation:
In the query [1, 4] there is no connection, between vertex 1 and vertex 4 in the graph so the result is "false."
Likewise in the query [2, 5] there is no link between vertex 2 and vertex 5 in the graph resulting in "false" again.
However with regards to the query [3, 6] a path does exist between vertex 3 and vertex 6 in the graph leading to a result of "true."Input: N = 8, K = 3, queries = [[1, 5], [2, 7], [4, 8], [3, 6]]
Output: false false true false
Explanation:
In the query [1, 5] there is no path connecting vertex 1 and vertex 5 in the graph; thus it yields a result of "false."
for the query [2,7] there is no connection between vertex 2 and vertex 7 in the graph; henceforth producing a result of "false."
Additionally, when considering the query [4,8] there is a path linking vertex 4 and vertex 8 within the graph. As a result, "true" is returned.
However, the fourth query[3,6] reveals no path connecting vertex three and six, within this graph. Therefore the outcome will be evaluated as"false."
Approach: To solve the problem follow the below idea:
The main idea is to utilize the Union Find (Disjoint Set Union) data structure, for creating and handling the connections in the graph. By going through values of z from K+1 to N and linking vertices that satisfy the given conditions the algorithm constructs the graph. Then for each inquiry it verifies if two vertices belong to the group by locating their root parents using path compression, in the Union Find operations.
The following steps are required to solve this problem:
- Create a parent array of size N+1 where each vertex is initially its own parent.
- Build the graph such that gcd(u,v) should be greater than K.
- To satisfy given condition Iterate from K+1 to N, considering each u.
- For each u, find vertice v that starts from 2*u to N.
- Use the union function to connect u and v by updating their parent to the same root parent.
- For each query [i, j]:
- Use the find function to find the root parent of i and j.
- If both vertices share the same root parent, they are connected; otherwise, they are not.
- Return a list of results, indicating connectivity for each query.
Below is the implementation for the above approach:
#include <iostream>
#include <vector>
using namespace std;
// Function to find the representative of a set using path compression
int find(vector<int>& parent, int i) {
if (parent[i] == -1) {
return i;
}
// Path compression
parent[i] = find(parent, parent[i]);
return parent[i];
}
// Function to perform union operation of two sets based on rank
void unionSets(vector<int>& parent, vector<int>& rank, int i, int j) {
int parent_i = find(parent, i);
int parent_j = find(parent, j);
if (parent_i != parent_j) {
if (rank[parent_i] < rank[parent_j]) {
parent[parent_i] = parent_j;
} else if (rank[parent_i] > rank[parent_j]) {
parent[parent_j] = parent_i;
} else {
parent[parent_j] = parent_i;
rank[parent_i]++;
}
}
}
// Function to check if two elements are connected in the graph
vector<bool> isConnected(int N, int K, vector<vector<int>>& queries) {
vector<int> parent(N + 1, -1);
vector<int> rank(N + 1, 0);
vector<bool> results(queries.size(), false);
// Build the graph using the given conditions
for (int z = K + 1; z <= N; z++) {
for (int u = z * 2; u <= N; u += z) {
unionSets(parent, rank, z, u);
}
}
// Check connectivity for each query
for (int idx = 0; idx < queries.size(); idx++) {
int i = queries[idx][0];
int j = queries[idx][1];
int parent_i = find(parent, i);
int parent_j = find(parent, j);
// Store true if connected, false otherwise
results[idx] = (parent_i == parent_j);
}
return results;
}
int main() {
// Input 1
int N1 = 6, K1 = 2;
vector<vector<int>> queries1 = {{1, 4}, {2, 5}, {3, 6}};
vector<bool> output1 = isConnected(N1, K1, queries1);
// Output 1
for (bool result : output1) {
cout << (result ? "true" : "false") << " ";
}
cout << endl;
// Input 2
int N2 = 8, K2 = 3;
vector<vector<int>> queries2 = {{1, 5}, {2, 7}, {4, 8}, {3, 6}};
vector<bool> output2 = isConnected(N2, K2, queries2);
// Output 2
for (bool result : output2) {
cout << (result ? "true" : "false") << " ";
}
cout << endl;
return 0;
}
// Java code for the above approach
import java.util.*;
public class Main {
// Function to find the representative of a set using
// path compression
private static int find(List<Integer> parent, int i)
{
if (parent.get(i) == -1) {
return i;
}
// Path compression
parent.set(i, find(parent, parent.get(i)));
return parent.get(i);
}
// Function to perform union operation of two sets based
// on rank
private static void unionSets(List<Integer> parent,
List<Integer> rank, int i,
int j)
{
int parent_i = find(parent, i);
int parent_j = find(parent, j);
if (parent_i != parent_j) {
if (rank.get(parent_i) < rank.get(parent_j)) {
parent.set(parent_i, parent_j);
}
else if (rank.get(parent_i)
> rank.get(parent_j)) {
parent.set(parent_j, parent_i);
}
else {
parent.set(parent_j, parent_i);
rank.set(parent_i, rank.get(parent_i) + 1);
}
}
}
// Function to check if two elements are connected in
// the graph
private static List<Boolean>
isConnected(int N, int K, List<List<Integer> > queries)
{
List<Integer> parent = new ArrayList<>(
Arrays.asList(new Integer[N + 1]));
List<Integer> rank = new ArrayList<>(
Arrays.asList(new Integer[N + 1]));
for (int i = 0; i <= N; i++) {
parent.set(i, -1);
rank.set(i, 0);
}
List<Boolean> results
= new ArrayList<>(queries.size());
// Build the graph using the given conditions
for (int z = K + 1; z <= N; z++) {
for (int u = z * 2; u <= N; u += z) {
unionSets(parent, rank, z, u);
}
}
// Check connectivity for each query
for (List<Integer> query : queries) {
int i = query.get(0);
int j = query.get(1);
int parent_i = find(parent, i);
int parent_j = find(parent, j);
// Store true if connected, false otherwise
results.add(parent_i == parent_j);
}
return results;
}
public static void main(String[] args)
{
// Input 1
int N1 = 6, K1 = 2;
List<List<Integer> > queries1
= new ArrayList<>(Arrays.asList(
new ArrayList<>(Arrays.asList(1, 4)),
new ArrayList<>(Arrays.asList(2, 5)),
new ArrayList<>(Arrays.asList(3, 6))));
List<Boolean> output1
= isConnected(N1, K1, queries1);
// Output 1
for (boolean result : output1) {
System.out.print(result ? "true " : "false ");
}
System.out.println();
// Input 2
int N2 = 8, K2 = 3;
List<List<Integer> > queries2
= new ArrayList<>(Arrays.asList(
new ArrayList<>(Arrays.asList(1, 5)),
new ArrayList<>(Arrays.asList(2, 7)),
new ArrayList<>(Arrays.asList(4, 8)),
new ArrayList<>(Arrays.asList(3, 6))));
List<Boolean> output2
= isConnected(N2, K2, queries2);
// Output 2
for (boolean result : output2) {
System.out.print(result ? "true " : "false ");
}
System.out.println();
}
}
// This code is contributed by Abhinav Mahajan (abhinav_m22)
# Function to find the representative of
# a set using path compression
def find(parent, i):
if parent[i] == -1:
return i
# Path compression
parent[i] = find(parent, parent[i])
return parent[i]
# Function to perform union operation of
# two sets based on rank
def union(parent, rank, i, j):
parent_i = find(parent, i)
parent_j = find(parent, j)
if parent_i != parent_j:
if rank[parent_i] < rank[parent_j]:
parent[parent_i] = parent_j
elif rank[parent_i] > rank[parent_j]:
parent[parent_j] = parent_i
else:
parent[parent_j] = parent_i
rank[parent_i] += 1
# Function to check if two elements are connected in the graph
def is_connected(N, K, queries):
# Initialize parent and rank arrays for
# disjoint-set data structure
parent = [-1] * (N + 1)
rank = [0] * (N + 1)
# Initialize results list to store query results
results = [False] * len(queries)
# Build the graph using the given conditions
for z in range(K + 1, N + 1):
for u in range(z * 2, N + 1, z):
union(parent, rank, z, u)
# Check connectivity for each query
for idx, query in enumerate(queries):
i, j = query
parent_i = find(parent, i)
parent_j = find(parent, j)
# Store True if connected, False otherwise
results[idx] = (parent_i == parent_j)
return results
# Input 1
N1, K1 = 6, 2
queries1 = [[1, 4], [2, 5], [3, 6]]
output1 = is_connected(N1, K1, queries1)
print(output1)
# Input 2
N2, K2 = 8, 3
queries2 = [[1, 5], [2, 7], [4, 8], [3, 6]]
output2 = is_connected(N2, K2, queries2)
print(output2)
using System;
using System.Collections.Generic;
class DisjointSet
{
// Function to find the representative of a set using path compression
private static int Find(List<int> parent, int i)
{
if (parent[i] == -1)
{
return i;
}
// Path compression
parent[i] = Find(parent, parent[i]);
return parent[i];
}
// Function to perform union operation of two sets based on rank
private static void UnionSets(List<int> parent, List<int> rank, int i, int j)
{
int parent_i = Find(parent, i);
int parent_j = Find(parent, j);
if (parent_i != parent_j)
{
if (rank[parent_i] < rank[parent_j])
{
parent[parent_i] = parent_j;
}
else if (rank[parent_i] > rank[parent_j])
{
parent[parent_j] = parent_i;
}
else
{
parent[parent_j] = parent_i;
rank[parent_i]++;
}
}
}
// Function to check if two elements are connected in the graph
private static List<bool> IsConnected(int N, int K, List<List<int>> queries)
{
List<int> parent = new List<int>(new int[N + 1]);
List<int> rank = new List<int>(new int[N + 1]);
List<bool> results = new List<bool>(queries.Count);
// Initialize parent and rank arrays
for (int i = 0; i <= N; i++)
{
parent[i] = -1;
rank[i] = 0;
}
// Build the graph using the given conditions
for (int z = K + 1; z <= N; z++)
{
for (int u = z * 2; u <= N; u += z)
{
UnionSets(parent, rank, z, u);
}
}
// Check connectivity for each query
foreach (List<int> query in queries)
{
int i = query[0];
int j = query[1];
int parent_i = Find(parent, i);
int parent_j = Find(parent, j);
// Store true if connected, false otherwise
results.Add(parent_i == parent_j);
}
return results;
}
static void Main()
{
// Input 1
int N1 = 6, K1 = 2;
List<List<int>> queries1 = new List<List<int>> { new List<int> { 1, 4 }, new List<int> { 2, 5 }, new List<int> { 3, 6 } };
List<bool> output1 = IsConnected(N1, K1, queries1);
// Output 1
Console.WriteLine(string.Join(" ", output1));
// Input 2
int N2 = 8, K2 = 3;
List<List<int>> queries2 = new List<List<int>> { new List<int> { 1, 5 }, new List<int> { 2, 7 }, new List<int> { 4, 8 }, new List<int> { 3, 6 } };
List<bool> output2 = IsConnected(N2, K2, queries2);
// Output 2
Console.WriteLine(string.Join(" ", output2));
}
}
// Function to find the representative of a set using path compression
function find(parent, i) {
if (parent[i] === -1) {
return i;
}
// Path compression
parent[i] = find(parent, parent[i]);
return parent[i];
}
// Function to perform union operation of two sets based on rank
function unionSets(parent, rank, i, j) {
const parent_i = find(parent, i);
const parent_j = find(parent, j);
if (parent_i !== parent_j) {
if (rank[parent_i] < rank[parent_j]) {
parent[parent_i] = parent_j;
} else if (rank[parent_i] > rank[parent_j]) {
parent[parent_j] = parent_i;
} else {
parent[parent_j] = parent_i;
rank[parent_i]++;
}
}
}
// Function to check if two elements are connected in the graph
function isConnected(N, K, queries) {
const parent = new Array(N + 1).fill(-1);
const rank = new Array(N + 1).fill(0);
const results = new Array(queries.length).fill(false);
// Build the graph using the given conditions
for (let z = K + 1; z <= N; z++) {
for (let u = z * 2; u <= N; u += z) {
unionSets(parent, rank, z, u);
}
}
// Check connectivity for each query
for (let idx = 0; idx < queries.length; idx++) {
const i = queries[idx][0];
const j = queries[idx][1];
const parent_i = find(parent, i);
const parent_j = find(parent, j);
// Store true if connected, false otherwise
results[idx] = parent_i === parent_j;
}
return results;
}
// Input 1
const N1 = 6;
const K1 = 2;
const queries1 = [[1, 4], [2, 5], [3, 6]];
const output1 = isConnected(N1, K1, queries1);
// Output 1
console.log(output1.map(result => result ? "true" : "false").join(" "));
// Input 2
const N2 = 8;
const K2 = 3;
const queries2 = [[1, 5], [2, 7], [4, 8], [3, 6]];
const output2 = isConnected(N2, K2, queries2);
// Output 2
console.log(output2.map(result => result ? "true" : "false").join(" "));
Output
false false true false false true false
Time Complexity: O(N + Q) where N is the number of elements, and Q is the number of queries.
Auxiliary Space: O(N)