Given an array, arr[] of size N, an integer P and a 2D array Q[][] consisting of queries of the following type:
- 1 L R B[R – L + 1]: The task for this query is to replace the subarray {arr[L], … arr[R] with the array B[] b given that any array element can be replaced at most P times.
- 2 X: The task for this query is to print arr[X].
Examples:
Input: arr[] = {3, 10, 4, 2, 8, 7}, P = 1, Q[][] = {{1, 0, 3, 3, 2, 1, 11}, {2, 3}, {1, 2, 3, 5, 7}, {2, 2} }
Output: 11 1
Explanation:
Query 1: Replacing the subarray {arr[0], …, arr[3]} with the array {3, 2, 1, 11} modifies arr[] to {3, 2, 1, 11, 8, 7}
Query 2: Print arr[3].
Query 3: Since P = 1, therefore the subarray {arr[2], …, arr[3]} can’t be replaced more than once.
Query 4: Print arr[2].Input: arr[] = {1, 2, 3, 4, 5}, P = 2, Q[][] = {{2, 0}, {1, 1, 3, 6, 7, 8}, {1, 3, 4, 10, 12}, {2, 4}}
Output: 1 12
Approach: The problem can be solved using the Union-Find algorithm. The idea is to traverse the array Q[][] and check if Q[0] is equal to 1 or not. If found to be true, then replace the subarray with the new array and whenever any array element has been replaced P times, then create a new subset using the union-find. Follow the steps below to solve the problem:
- Initialize an array, say visited[], where visited[i] check index i is present in any disjoint subset or not.
- Initialize an array, say count[], where count[i] stores how many times arr[i] has been replaced.
- Initialize an array, say last[], to store the largest element of each disjoint subset.
- Initialize an array, say parent[], to store the smallest element of each disjoint subset.
- Traverse the Q[][] array and for each query check if Q[i][0] == 1 or not. If found to be true then perform the following operations:
- Iterate over the range [ Q[i][1], Q[i][2] ] using variable low and check if visited[low] is true or not. If found to be true then find the parent of that subset where low present, find the largest element present in the subset.
- Otherwise, check if count[low] is less than P or not. If found to be true then replace the value of arr[low] with the corresponding value.
- Otherwise, check if count[low] is equal to P or not. If found to be true then create a new disjoint subset of the current index.
- Otherwise, print arr[Q[i][1]]].
Below is the implementation of the above approach:
C++
// C++ program to implement // the above approach #include <bits/stdc++.h> using namespace std; // visited[i]: Check index i is present // in any disjoint subset or not. bool visited[100001]; // Store the smallest element // of each disjoint subset int parent[100001]; // count[i]: Stores the count // of replacements of arr[i] int last[100001]; // Store the largest element // of each disjoint subset int Count[100001]; // Function to find the smallest // element of the subset int findParent( int i) { // Base Case if (parent[i] == i) { return i; } // Stores smallest element // of parent[u] return parent[i] = findParent(parent[i]); } // Function to perform union operation void Union( int u, int v, int arr[]) { // Stores smallest element // of subset containing u int p1=findParent(u); // Stores smallest element // of subset containing v int p2=findParent(v); // Update parent[p2] parent[p2] = p1; // Update last[p1] last[p1] = last[p2]; } // Function to create a new subset void newUnion( int low, int high, int arr[]) { // Iterate over // the range [low + 1, high] for ( int i = low + 1; i <= high; i++) { // Perform union operation // on low Union(low, i, arr); } // If just smaller element of low // is present in any of the subset if (low > 0 && visited[low - 1]) { // Perform union on (low - 1) Union(low - 1, low, arr); } // If just greater element of high // is present in any of the subset int N = sizeof (arr) / sizeof (arr[0]); if (high < N - 1 && visited[high + 1]) { // Perform union on high Union(high, high + 1,arr); } } // Function to find the next index // to be processed after visiting low int findJumpLength( int low) { // Stores smallest index of // the subset where low present int p = findParent(low); // Stores next index // to be processed int nextIndex = last[p] + 1; // Stores difference between // low and nextIndex int jump = (nextIndex - low); // Return jump return jump; } // Function to perform the query of type 1 void processTypeOneQuery(vector< int > query, int arr[], int P) { // Stores the value of L int low = query[1]; // Stores the value of R int high = query[2]; // Stores leftmost index of the // subarray for which a new // subset can be generated int left = -1; // Stores index of // the query[] array int j = 3; // Iterate over the // range [low, high] while (low <= high) { // If low is present in // any of the subset if (visited[low]) { // If no subset created for // the subarray arr[left...low - 1] if (left != -1) { // Create a new subset Union(low - 1, left, arr); // Update left left = -1; } // Stores next index to be // processed int jump = findJumpLength(low); // Update low low += jump; // Update j j += jump; } // If arr[low] has been // already replaced P times else if (Count[low] == P) { // If already subset // created for left if (left == -1) { // Update left left = low; } // Mark low as an element // of any subset visited[low] = true ; // Update low low++; // Update j j++; } // If arr[low] has been replaced // less than P times else { // If no subset created for // the subarray arr[left...low - 1] if (left != -1) { // Create a new subset Union(low - 1, left, arr); // Update left left = -1; } // Replace arr[low] with // the corresponding value arr[low] = query[j]; // Update count[low] Count[low]++; // Update low low++; // Update j j++; } } // If no subset has been created for // the subarray arr[left...low - 1] if (left != -1) { // Create a new subset newUnion(left, high, arr); } } // Function to process all the given Queries void processQueries( int arr[], int P, vector<vector< int >> Q) { // Traverse the queries[][] array for ( int i = 0; i < Q.size(); i++) { // Stores the current query vector< int > query = Q[i]; // If query of type is 1 if (query[0] == 1) { // Perform the query of type 1 processTypeOneQuery(query,arr,P); } // If query of type is 2 else { // Stores 2nd element of // current query int index = query[1]; // Print arr[index] cout << arr[index] << endl; } } } // Function to find all the queries static vector<vector< int >> getQueries() { // Stores all the queries vector<vector< int >> Q; // Initialize all queries array< int , 7> query1 = { 1, 0, 3, 3, 2, 1, 11 }; array< int , 2> query2 = { 2, 3 }; array< int , 5> query3 = { 1, 2, 3, 5, 7 }; array< int , 2> query4 = { 2, 2 }; // Insert all queries Q.push_back(vector< int >(query1.begin(), query1.end())); Q.push_back(vector< int >(query2.begin(), query2.end())); Q.push_back(vector< int >(query3.begin(), query3.end())); Q.push_back(vector< int >(query4.begin(), query4.end())); // Return all queries return Q; } // Driver Code int main() { int arr[] = { 3, 10, 4, 2, 8, 7 }; int N = sizeof (arr) / sizeof (arr[0]); int P=1; vector<vector< int >> Q; // Initialize parent[] and // last[] array for ( int i = 0; i < N; i++) { // Update parent[i] parent[i] = i; // Update last[i] last[i] = i; } Q=getQueries(); processQueries(arr, P, Q); } // This code is contributed by Utkarsh |
Java
// Java program to implement // the above approach import java.io.*; import java.util.*; class GFG { // visited[i]: Check index i is present // in any disjoint subset or not. static boolean [] visited; // Store the smallest element // of each disjoint subset static int [] parent; // count[i]: Stores the count // of replacements of arr[i] static int [] last; // Store the largest element // of each disjoint subset static int [] count; // Function to process all the given Queries static void processQueries( int [] arr, int P, List<List<Integer> > Q) { // Traverse the queries[][] array for ( int i = 0 ; i < Q.size(); i++) { // Stores the current query List<Integer> query = Q.get(i); // If query of type is 1 if (query.get( 0 ) == 1 ) { // Perform the query of type 1 processTypeOneQuery(query, arr, P); } // If query of type is 2 else { // Stores 2nd element of // current query int index = query.get( 1 ); // Print arr[index] System.out.println(arr[index]); } } } // Function to perform the query of type 1 static void processTypeOneQuery( List<Integer> query, int [] arr, int P) { // Stores the value of L int low = query.get( 1 ); // Stores the value of R int high = query.get( 2 ); // Stores leftmost index of the // subarray for which a new // subset can be generated int left = - 1 ; // Stores index of // the query[] array int j = 3 ; // Iterate over the // range [low, high] while (low <= high) { // If low is present in // any of the subset if (visited[low]) { // If no subset created for // the subarray arr[left...low - 1] if (left != - 1 ) { // Create a new subset newUnion(left, low - 1 , arr.length); // Update left left = - 1 ; } // Stores next index to be // processed int jump = findJumpLength(low); // Update low low += jump; // Update j j += jump; } // If arr[low] has been // already replaced P times else if (count[low] == P) { // If already subset // created for left if (left == - 1 ) { // Update left left = low; } // Mark low as an element // of any subset visited[low] = true ; // Update low low++; // Update j j++; } // If arr[low] has been replaced // less than P times else { // If no subset created for // the subarray arr[left...low - 1] if (left != - 1 ) { // Create a new subset newUnion(left, low - 1 , arr.length); // Update left left = - 1 ; } // Replace arr[low] with // the corresponding value arr[low] = query.get(j); // Update count[low] count[low]++; // Update low low++; // Update j j++; } } // If no subset has been created for // the subarray arr[left...low - 1] if (left != - 1 ) { // Create a new subset newUnion(left, high, arr.length); } } // Function to find the next index // to be processed after visiting low static int findJumpLength( int low) { // Stores smallest index of // the subset where low present int p = findParent(low); // Stores next index // to be processed int nextIndex = last[p] + 1 ; // Stores difference between // low and nextIndex int jump = (nextIndex - low); // Return jump return jump; } // Function to create a new subset static void newUnion( int low, int high, int N) { // Iterate over // the range [low + 1, high] for ( int i = low + 1 ; i <= high; i++) { // Perform union operation // on low union(low, i); } // If just smaller element of low // is present in any of the subset if (low > 0 && visited[low - 1 ]) { // Perform union on (low - 1) union(low - 1 , low); } // If just greater element of high // is present in any of the subset if (high < N - 1 && visited[high + 1 ]) { // Perform union on high union(high, high + 1 ); } } // Function to find the smallest // element of the subset static int findParent( int u) { // Base Case if (parent[u] == u) return u; // Stores smallest element // of parent[u return parent[u] = findParent(parent[u]); } // Function to perform union operation static void union( int u, int v) { // Stores smallest element // of subset containing u int p1 = findParent(u); // Stores smallest element // of subset containing u int p2 = findParent(v); // Update parent[p2] parent[p2] = p1; // Update last[p1] last[p1] = last[p2]; } // Function to find all the queries static List<List<Integer> > getQueries() { // Stores all the queries List<List<Integer> > Q = new ArrayList<List<Integer> >(); // Initialize all queries Integer[] query1 = { 1 , 0 , 3 , 3 , 2 , 1 , 11 }; Integer[] query2 = { 2 , 3 }; Integer[] query3 = { 1 , 2 , 3 , 5 , 7 }; Integer[] query4 = { 2 , 2 }; // Insert all queries Q.add(Arrays.asList(query1)); Q.add(Arrays.asList(query2)); Q.add(Arrays.asList(query3)); Q.add(Arrays.asList(query4)); // Return all queries return Q; } // Driver Code public static void main(String[] args) { int [] arr = { 3 , 10 , 4 , 2 , 8 , 7 }; int N = arr.length; int P = 1 ; parent = new int [N]; last = new int [N]; count = new int [N]; visited = new boolean [N]; // Initialize parent[] and // last[] array for ( int i = 0 ; i < parent.length; i++) { // Update parent[i] parent[i] = i; // Update last[i] last[i] = i; } List<List<Integer> > Q = getQueries(); processQueries(arr, P, Q); } } |
Python3
# Python3 program to implement # the above approach # visited[i]: Check index i is present # in any disjoint subset or not. visited = [] # Store the smallest element # of each disjoint subset parent = [] # count[i]: Stores the count # of replacements of arr[i] last = [] # Store the largest element # of each disjoint subset count = [] # Function to process all the given Queries def processQueries(arr, P, Q): # Traverse the [,]queries array for i in range ( len (Q)): # Stores the current query query = Q[i]; # If query of type is 1 if (query[ 0 ] = = 1 ): # Perform the query of type 1 processTypeOneQuery(query, arr, P); # If query of type is 2 else : # Stores 2nd element of # current query index = query[ 1 ]; # Print arr[index] print (arr[index]); # Function to perform the query of type 1 def processTypeOneQuery(query, arr, P): # Stores the value of L low = query[ 1 ]; # Stores the value of R high = query[ 2 ]; # Stores leftmost index of the # subarray for which a new # subset can be generated left = - 1 ; # Stores index of # the query[] array j = 3 ; # Iterate over the # range [low, high] while (low < = high): # If low is present in # any of the subset if (visited[low]): # If no subset created for # the subarray arr[left...low - 1] if (left ! = - 1 ): # Create a new subset newUnion(left, low - 1 , len (arr)); # Update left left = - 1 ; # Stores next index to be # processed jump = findJumpLength(low); # Update low low + = jump; # Update j j + = jump; # If arr[low] has been # already replaced P times elif (count[low] = = P): # If already subset # created for left if (left = = - 1 ): # Update left left = low; # Mark low as an element # of any subset visited[low] = True ; # Update low low + = 1 # Update j j + = 1 # If arr[low] has been replaced # less than P times else : # If no subset created for # the subarray arr[left...low - 1] if (left ! = - 1 ): # Create a new subset newUnion(left, low - 1 , len (arr)); # Update left left = - 1 ; # Replace arr[low] with # the corresponding value arr[low] = query[j]; # Update count[low] count[low] + = 1 # Update low low + = 1 # Update j j + = 1 # If no subset has been created for # the subarray arr[left...low - 1] if (left ! = - 1 ): # Create a new subset newUnion(left, high, len (arr)); # Function to find the next index # to be processed after visiting low def findJumpLength(low): # Stores smallest index of # the subset where low present p = findParent(low); # Stores next index # to be processed nextIndex = last[p] + 1 ; # Stores difference between # low and nextIndex jump = (nextIndex - low); # Return jump return jump; # Function to create a new subset def newUnion(low, high,N): # Iterate over # the range [low + 1, high] for i in range (low + 1 ,high + 1 ): # Perform union operation # on low union(low, i); # If just smaller element of low # is present in any of the subset if (low > 0 and visited[low - 1 ]): # Perform union on (low - 1) union(low - 1 , low); # If just greater element of high # is present in any of the subset if (high < N - 1 and visited[high + 1 ]): # Perform union on high union(high, high + 1 ); # Function to find the smallest # element of the subset def findParent(u): # Base Case if (parent[u] = = u): return u; # Stores smallest element # of parent[u parent[u] = findParent(parent[u]); return parent[u] # Function to perform union operation def union(u, v): # Stores smallest element # of subset containing u p1 = findParent(u); # Stores smallest element # of subset containing u p2 = findParent(v); # Update parent[p2] parent[p2] = p1; # Update last[p1] last[p1] = last[p2]; # Function to find all the queries def getQueries(): # Stores all the queries Q = [] # Initialize all queries query1 = [ 1 , 0 , 3 , 3 , 2 , 1 , 11 ] query2 = [ 2 , 3 ] query3 = [ 1 , 2 , 3 , 5 , 7 ] query4 = [ 2 , 2 ] # Insert all queries Q.append(query1) Q.append(query2) Q.append(query3) Q.append(query4) # Return all queries return Q; # Driver Code if __name__ = = '__main__' : arr = [ 3 , 10 , 4 , 2 , 8 , 7 ] N = len (arr) P = 1 ; parent = [i for i in range (N)] last = [i for i in range (N)] count = [ 0 for i in range (N)] visited = [ False for i in range (N)] Q = getQueries(); processQueries(arr, P, Q); # This code is contributed by rutvik_56. |
C#
// C# program to implement // the above approach using System; using System.Collections.Generic; public class GFG { // visited[i]: Check index i is present // in any disjoint subset or not. static bool [] visited; // Store the smallest element // of each disjoint subset static int [] parent; // count[i]: Stores the count // of replacements of arr[i] static int [] last; // Store the largest element // of each disjoint subset static int [] count; // Function to process all the given Queries static void processQueries( int [] arr, int P, List<List< int > > Q) { // Traverse the [,]queries array for ( int i = 0; i < Q.Count; i++) { // Stores the current query List< int > query = Q[i]; // If query of type is 1 if (query[0] == 1) { // Perform the query of type 1 processTypeOneQuery(query, arr, P); } // If query of type is 2 else { // Stores 2nd element of // current query int index = query[1]; // Print arr[index] Console.WriteLine(arr[index]); } } } // Function to perform the query of type 1 static void processTypeOneQuery( List< int > query, int [] arr, int P) { // Stores the value of L int low = query[1]; // Stores the value of R int high = query[2]; // Stores leftmost index of the // subarray for which a new // subset can be generated int left = -1; // Stores index of // the query[] array int j = 3; // Iterate over the // range [low, high] while (low <= high) { // If low is present in // any of the subset if (visited[low]) { // If no subset created for // the subarray arr[left...low - 1] if (left != -1) { // Create a new subset newUnion(left, low - 1, arr.Length); // Update left left = -1; } // Stores next index to be // processed int jump = findJumpLength(low); // Update low low += jump; // Update j j += jump; } // If arr[low] has been // already replaced P times else if (count[low] == P) { // If already subset // created for left if (left == -1) { // Update left left = low; } // Mark low as an element // of any subset visited[low] = true ; // Update low low++; // Update j j++; } // If arr[low] has been replaced // less than P times else { // If no subset created for // the subarray arr[left...low - 1] if (left != -1) { // Create a new subset newUnion(left, low - 1, arr.Length); // Update left left = -1; } // Replace arr[low] with // the corresponding value arr[low] = query[j]; // Update count[low] count[low]++; // Update low low++; // Update j j++; } } // If no subset has been created for // the subarray arr[left...low - 1] if (left != -1) { // Create a new subset newUnion(left, high, arr.Length); } } // Function to find the next index // to be processed after visiting low static int findJumpLength( int low) { // Stores smallest index of // the subset where low present int p = findParent(low); // Stores next index // to be processed int nextIndex = last[p] + 1; // Stores difference between // low and nextIndex int jump = (nextIndex - low); // Return jump return jump; } // Function to create a new subset static void newUnion( int low, int high, int N) { // Iterate over // the range [low + 1, high] for ( int i = low + 1; i <= high; i++) { // Perform union operation // on low union(low, i); } // If just smaller element of low // is present in any of the subset if (low > 0 && visited[low - 1]) { // Perform union on (low - 1) union(low - 1, low); } // If just greater element of high // is present in any of the subset if (high < N - 1 && visited[high + 1]) { // Perform union on high union(high, high + 1); } } // Function to find the smallest // element of the subset static int findParent( int u) { // Base Case if (parent[u] == u) return u; // Stores smallest element // of parent[u return parent[u] = findParent(parent[u]); } // Function to perform union operation static void union( int u, int v) { // Stores smallest element // of subset containing u int p1 = findParent(u); // Stores smallest element // of subset containing u int p2 = findParent(v); // Update parent[p2] parent[p2] = p1; // Update last[p1] last[p1] = last[p2]; } // Function to find all the queries static List<List< int > > getQueries() { // Stores all the queries List<List< int > > Q = new List<List< int > >(); // Initialize all queries int [] query1 = { 1, 0, 3, 3, 2, 1, 11 }; int [] query2 = { 2, 3 }; int [] query3 = { 1, 2, 3, 5, 7 }; int [] query4 = { 2, 2 }; // Insert all queries Q.Add( new List< int >(query1)); Q.Add( new List< int >(query2)); Q.Add( new List< int >(query3)); Q.Add( new List< int >(query4)); // Return all queries return Q; } // Driver Code public static void Main(String[] args) { int [] arr = { 3, 10, 4, 2, 8, 7 }; int N = arr.Length; int P = 1; parent = new int [N]; last = new int [N]; count = new int [N]; visited = new bool [N]; // Initialize parent[] and // last[] array for ( int i = 0; i < parent.Length; i++) { // Update parent[i] parent[i] = i; // Update last[i] last[i] = i; } List<List< int > > Q = getQueries(); processQueries(arr, P, Q); } } // This code is contributed by Amit Katiyar |
11 1
Time Complexity: O(N + |Q| * P)
Auxiliary Space: O(N)
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!