Queries to count array elements greater than or equal to given number with updates


An array can be successfully updated and put through to range queries thanks to segment trees. With updates, we can use data structure known segment tree to count no. of elements in Array which are larger than or same as no.

  • Query − Find out how many items exist in [l, r] range that are larger than or similar to x.

    • Give 0 if range [l, r] lies entirely beyond segment that current node of segment tree represents.

    • Count no. of elements in range [l, r] which is larger than or similar to x if interval [l, r] entirely within segment represented by current node of segment tree.

    • If not, recursively ping the current node's left and right children, returning the total of counts gathered.

  • Update − To element at index i, add value of v. We apply following algorithm to this update −

    • If segment tree's current node show range without index i, don’t do anything.

    • Update count of element larger than or same as x in interval that current node of segment tree represents by incrementing it if value at index i is larger than or same x, and then recursively update left and right children of current node.

    • We can run queries in root node of segment tree with range [0, n-1], here n is total no. of entries in array that is larger than or same as x.

Syntax

1. Create the segment tree and array from scratch −

int M; 
int Z[M]; 
int TreE[4*M]; 
BUILD (1, 0, M-1, Z, TreE); 

2. Carry update(change) procedure −

Void change (int NoDe, BeGiN,EnD,IdX,VaL,TreE []) {
   if (BeGiN==EnD) {
      Z[IdX]=VaL;
      TreE[NoDe]=VaL;
   } else {
      int MiD= (BeGiN + EnD)/2;
      if (BeGiN<=IdX && IdX<=MiD)
         change (2*NoDe, BeGiN, MiD, IdX, VaL, TreE);
      else
         change (2*NoDe+1, MiD+1, EnD, IdX, VaL, TreE);
      TreE[NoDe] = TreE[2*NoDe] + TreE[2*NoDe+1];
   }
}

3. Execute the following query operation −

int QUERY(int NoDe, BeGiN, EnD, L, R, K, TreE []) {
   if(sTaRT > EnD || BeGiN > R || EnD < L)
      return 0;
   if(BeGiN == EnD)
      return A[BeGiN] >= K;
   int MiD = (BeGiN + EnD) / 2;
   return QUERY(2*NoDe, BeGiN, MiD, L, R, K, TreE) + QUERY (2*NoDe+1, MiD+1, EnD, L, R, K, TreE);
}

4. Use query operation to count no. of elements larger than or same as specified value, update operation to update array and segment tree −

int IdX, VaL; 
change(1, 0, n-1, IX, VaL, TreE);
int L, R, K; 
int count = QUERY(1, 0, M-1, L, R, K, TreE);

Algorithm

Using updates, following is one possible approach to count no. of array members larger than or same as specified value −

  • Step 1 − Create array A with size of n to hold starting values.

  • Step 2 − To show range minimum query, initialize segment tree T of size 4*n.

  • Step 3 − Use function build (T, A, 1, 0, n-1) for create segment tree T, where build(T, A, v, tl, tr) create segment tree T for range [tl, tr] using values in A and puts result in node v of T.

  • Step 4 − Create array C from size of n and initialize it with counts of items greater than or equal to specified number.

  • Step 5 − Create segment tree S with starting size of 4*n to represent counts query's range sum.

  • Step 6 − Call function build (S, C, 1, 0, n-1), where build(S, C, v, tl, tr) create segment tree S for range [tl, tr] using values in C and keep result in node v of S.

  • Step 7 − In response to each "count elements larger than or same as x" query −

  • To find lowest value in range [l, r] of array A, call function query(T, 1, 0, n-1, l, r). Suppose the outcome is m.

    If m is more than or equal to x, use function query(S, 1, 0, n-1, l, r) to obtain total no. of entries in interval [l, r] of array C that are larger than or same as x. Let outcome be c.

    If not, set c to 0.

  • Step 8 − Every change of type "set value of A[i] to v" −

  • Update node v of segment tree T over range [tl, tr], call function update (T, v, tl, tr, i, val), where update (T, v, tl, tr, i, val) change node v of segment tree T by setting value at index i to val.

    Use function update (S, 1, 0, n-1, i, (v >= x)) update segment tree node v for range [tl, tr], where update(S, v, tl, tr, i, val) update node v by adding val to count of items larger than or same as x.

  • Step 9 − Repeat steps 7 and 8 again.

Approaches to Follow

Approach 1

In example, we define vector of type int to represent our array and threshold value of type int above or equal to which we wish to count number of entries. The counttheElements function is then used to produce the initial array and no.of elements that are more than or equal to threshold.

The updatetheElement function, which accepts array, index of element to update, and new value for that element as arguments, is then used to update an element in array. Finally, we use counttheElements method once more to output revised array and new count of elements larger than or equal to threshold.

Example 1

#include <iostream>
#include <vector>
using namespace std;
void updatethenumber(vector<int>& ara, int index, int NEWValues) {
   ara[index] = NEWValues;
}
int countthenumber(vector<int>& ara, int threshold) {
   int cont = 0;
   for (int i = 0; i < ara.size(); i++) {
      if (ara[i] >= threshold) {
         cont++;
      }
   }return cont;
}
int main () {
   vector<int> ara = {3, 6, 2,8, 4, 7} ;
   int threshold = 5;
   cout << "Initial array: ";
   for(int i = 0;i < ara.size();i++) {
      cout << ara[i] << " ";
   }cout<<endl;
   cout<<"Number of elements >= "<<threshold<< ": ";
   cout<<countthenumber(ara, threshold)<<endl;
   cout<<"Updating element at index 2 to value 9..."<<endl;
   updatethenumber(ara,2,9) ;
   cout<<"Updated array: " ;
   for(int i = 0;i<ara.size();i++) {
      cout<<ara[i]<< " ";
   } cout<<endl ;
   cout<<"Number of elements >= " << threshold << ": " ;
   cout<<countthenumber(ara, threshold)<<endl;
   return 0;
}

Output

Initial array: 3 6 2 8 4 7 
Number of elements >= 5: 3
Updating element at index 2 to value 9
Updated array: 3 6 9 8 4 7 
Number of elements >= 5: 4

Approach -2

The role for every query is to count no. of array (items) that are larger than or same as Query [i], decrement all those values by M, then run remaining questions on updated array. The inputs are two arrays, array [] and Query [] of sizes N and Q.

Sort array [] in ascending order first.

Find element, say l, in first place that has an element greater or equal to query[i].

If there are no such elements, then 0 will be returned as response. Otherwise, the response will be N - l.

The last step is to subtract M from each element in the supplied range and change segment tree in interval l to N - 1.

Example-2

#include <bits/stdc++.h>
using namespace std;

void build(vector<int>& tosum, vector<int>& a, int l, int r, int rlt){
   if (l == r) {
      tosum[rlt] = a [l - 1];
      return;
   }
   int m = (l + r) >> 1;
   build (tosum, a, l, m, rlt << 1);
   build (tosum, a, m + 1, r, rlt << 1 | 1);
}
void push(vector<int>& tosum, vector<int>& toadd, int rlt, int ln, int rn){
   if (toadd[rlt]) {
      toadd [rlt<< 1] += toadd[rlt];
      toadd [rlt << 1 | 1] += toadd[rlt];
      tosum [rlt<< 1] += toadd[rlt] * ln;
      tosum [rlt << 1 | 1] += toadd[rlt] * rn;
      toadd[rlt] = 0;
   }
}
void update(vector<int>& tosum, vector<int>& toadd, int L, int R, int C, int l,int r, int rlt){
   if (L <= l && r <= R) {
      tosum[rlt] += C * (r - l + 1);
      toadd[rlt] += C;
      return;
   }
   int m = (l + r) >> 1;
   push (tosum, toadd, rlt, m - l + 1,
   r - m);
   if (L <= m)
      update (tosum, toadd, L, R, C, l, m,
      rlt << 1);
   if (R > m)
      update (tosum, toadd, L, R, C, m + 1, r,
      rlt << 1 | 1);
}
int query(vector<int>& tosum,
   vector<int>& toadd,
   int L, int R, int l,
   int r, int rlt){
   if (L <= l && r <= R) {
      return tosum[rlt];
   }
   int m = (l + r) >> 1;
   push (tosum, toadd, rlt, m - l + 1,
   r - m);
   int ans = 0;
   if (L <= m)
   ans += query (tosum, toadd, L, R, l, m,
   rlt << 1);
   if (R > m)
   ans += query (tosum, toadd, L, R, m + 1, r,
   rlt << 1 | 1);
   return ans;
}
void sequMaint(int n, int q,vector<int>& a,vector<int>& b,int m){
   sort(a.begin(), a.end());
   vector<int> tosum, toadd, ans;
   tosum.assign(n << 2, 0);
   toadd.assign(n << 2, 0);
   build (tosum, a, 1, n, 1);
   for (int i = 0; i < q; i++) {
      int l = 1, r = n, pos = -1;
      while (l <= r) {
         int m = (l + r) >> 1;
         if (query (tosum, toadd, m, m, 1, n, 1)
         >= b[i]) {
            r = m - 1;
            pos = m;
         }
         else {
            l = m + 1;
         }
      }
      if (pos == -1)
      ans.push_back(0);
      else {
         ans.push_back(n - pos + 1);
         update (tosum, toadd, pos, n, -m,
         1, n, 1);
      }
   }
   for (int i = 0; i < ans.size(); i++) {
      cout << ans[i] << " ";
   }
}
int main (){
   int A = 4;
   int B = 3;
   int C = 1;
   vector<int> array = {1, 2, 3, 4};
   vector<int> query = {4, 3, 1};
   sequMaint(A, B, array, query, C);
   return 0;
}

Output

1 2 4

Conclusion

In conclusion, segment trees can be successfully used to count no. of elements in an array that are larger than or equal to fixed value with updates. We use lazy propagation to update segment tree instead of updating every node. Updates to node are done during lazy propagation until they are required. Overall, we can efficiently count no. of elements in array that are more than or equal to certain value with changes by using segment tree with lazy propagation.

Updated on: 10-May-2023

399 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements