/* Java Program to answer Q queries to
find number of times an element x
appears x times in a Query subarray */
import java.util.*;
public class GFG {
// Variable to represent block size.
// This is made global so compare()
// of sort can use it.
static int block;
// Structure to represent a query range
static class Query {
int L, R, index;
Query(int L, int R, int index) {
this.L = L;
this.R = R;
this.index = index;
}
}
/* Function used to sort all queries
so that all queries of same block
are arranged together and within
a block, queries are sorted in
increasing order of R values. */
static boolean compare(Query x, Query y) {
// Different blocks, sort by block.
if (x.L / block != y.L / block) {
return x.L / block < y.L / block;
}
// Same block, sort by R value
return x.R < y.R;
}
/* Inserts element (x) into current range
and updates current answer */
static void add(int x, int[] currentAns, HashMap<Integer, Integer> freq) {
// increment frequency of this element
freq.put(x, freq.getOrDefault(x, 0) + 1);
// if this element was previously
// contributing to the currentAns,
// decrement currentAns
if (freq.get(x) == (x + 1)) {
currentAns[0]--;
// if this element has frequency
// equal to its value, increment
// currentAns
} else if (freq.get(x) == x) {
currentAns[0]++;
}
}
/* Removes element (x) from current
range btw L and R and updates
current Answer */
static void remove(int x, int[] currentAns, HashMap<Integer, Integer> freq) {
// decrement frequency of this element
freq.put(x, freq.get(x) - 1);
// if this element has frequency equal
// to its value, increment currentAns
if (freq.get(x) == x) {
currentAns[0]++;
// if this element was previously
// contributing to the currentAns
// decrement currentAns
} else if (freq.get(x) == (x - 1)) {
currentAns[0]--;
}
}
/* Utility Function to answer all queries
and build the ans array in the original
order of queries */
static void queryResultsUtil(int[] a, Query[] q, int[] ans, int m) {
// map to store freq of each element
HashMap<Integer, Integer> freq = new HashMap<>();
// Initialize current L, current R
// and current sum
int currL = 0, currR = 0;
int[] currentAns = {0};
// Traverse through all queries
for (int i = 0; i < m; i++) {
int L = q[i].L, R = q[i].R;
int index = q[i].index;
// Remove extra elements of previous
// range. For example if previous
// range is [0, 3] and current range
// is [2, 5], then a[0] and a[1] are
// removed
while (currL < L) {
remove(a[currL], currentAns, freq);
currL++;
}
// Add Elements of current Range
while (currL > L) {
currL--;
add(a[currL], currentAns, freq);
}
while (currR <= R) {
add(a[currR], currentAns, freq);
currR++;
}
// Remove elements of previous range. For example
// when previous range is [0, 10] and current range
// is [3, 8], then a[9] and a[10] are Removed
while (currR > R + 1) {
currR--;
remove(a[currR], currentAns, freq);
}
// Store current ans as the Query ans for
// Query number index
ans[index] = currentAns[0];
}
}
/* Wrapper for queryResultsUtil() and outputs the
ans array constructed by answering all queries */
static void queryResults(int[] a, int n, Query[] q, int m) {
// Find block size
block = (int) Math.sqrt(n);
Arrays.sort(q, new Comparator<Query>() {
@Override
public int compare(Query x, Query y) {
// Different blocks, sort by block.
if (x.L / block != y.L / block)
return Integer.compare(x.L / block, y.L / block);
// Same block, sort by R value
return Integer.compare(x.R, y.R);
}
});
int[] ans = new int[m];
queryResultsUtil(a, q, ans, m);
for (int i = 0; i < m; i++) {
System.out.println("Answer for Query " + (i + 1) + " = " + ans[i]);
}
}
public static void main(String[] args) {
int[] A = {1, 2, 2, 3, 3, 3};
int n = A.length;
// 2D array of queries with 2 columns
Query[] queries = {
new Query(0, 1, 0),
new Query(1, 1, 1),
new Query(0, 2, 2),
new Query(1, 3, 3),
new Query(3, 5, 4),
new Query(0, 5, 5)
};
// calculating number of queries
int q = queries.length;
// Print result for each Query
queryResults(A, n, queries, q);
}
}
//this code is contributed by bhardwajji