📌  相关文章
📜  查询以更新方式对大于或等于给定数字的数组元素进行计数

📅  最后修改于: 2021-04-17 10:25:31             🧑  作者: Mango

给定两个分别具有大小NQ的数组arr []query []以及一个整数M ,每个查询的任务是计算大于或等于query [i]的数组元素的数量并减少所有此类元素用M编号,并在更新后的数组上执行其余查询。
例子:

天真的方法:最简单的方法是遍历每个查询的整个数组,并检查当前数组元素是否大于或等于query [i] 。对于满足上述条件的元素,将其减去M并增加计数。最后打印每个查询的计数。
时间复杂度: O(Q * N)
辅助空间: O(1)

高效的方法:可以使用段树解决该问题。

  1. 首先,以非降序对数组arr []进行排序。
  2. 现在,找到元素的第一个位置,即l ,它包含一个元素> = query [i]
  3. 如果不存在此类元素,则该查询的答案将为0 。否则答案将为N – l
  4. 最后,将给定范围l中的段树更新为N – 1,并从给定范围中的所有元素中减去M。

下面是上述方法的实现:

C++
// C++ program for the above approach
#include 
using namespace std;
 
// Function to build a segment tree
void build(vector& sum,
           vector& a,
           int l, int r, int rt)
{
    // Check for base case
    if (l == r) {
        sum[rt] = a[l - 1];
        return;
    }
 
    // Find mid point
    int m = (l + r) >> 1;
 
    // Recursively build the
    // segment tree
    build(sum, a, l, m, rt << 1);
    build(sum, a, m + 1, r, rt << 1 | 1);
}
 
// Function for push down operation
// on the segment tree
void pushDown(vector& sum,
              vector& add,
              int rt, int ln, int rn)
{
    if (add[rt]) {
        add[rt << 1] += add[rt];
        add[rt << 1 | 1] += add[rt];
        sum[rt << 1] += add[rt] * ln;
        sum[rt << 1 | 1] += add[rt] * rn;
        add[rt] = 0;
    }
}
 
// Function to update the segment tree
void update(vector& sum,
            vector& add,
            int L, int R, int C, int l,
            int r, int rt)
{
    // Complete overlap
    if (L <= l && r <= R) {
        sum[rt] += C * (r - l + 1);
        add[rt] += C;
        return;
    }
 
    // Find mid
    int m = (l + r) >> 1;
 
    // Perform push down operation
    // on segment tree
    pushDown(sum, add, rt, m - l + 1,
             r - m);
 
    // Recursively update the segment tree
    if (L <= m)
        update(sum, add, L, R, C, l, m,
               rt << 1);
 
    if (R > m)
        update(sum, add, L, R, C, m + 1, r,
               rt << 1 | 1);
}
 
// Function to process the query
int query(vector& sum,
          vector& add,
          int L, int R, int l,
          int r, int rt)
{
    // Base case
    if (L <= l && r <= R) {
        return sum[rt];
    }
 
    // Find mid
    int m = (l + r) >> 1;
 
    // Perform push down operation
    // on segment tree
    pushDown(sum, add, rt, m - l + 1,
             r - m);
 
    int ans = 0;
 
    // Recursively calculate the result
    // of the query
    if (L <= m)
        ans += query(sum, add, L, R, l, m,
                     rt << 1);
    if (R > m)
        ans += query(sum, add, L, R, m + 1, r,
                     rt << 1 | 1);
 
    // Return the result
    return ans;
}
 
// Function to count the numbers
// which are greater than the given query
void sequenceMaintenance(int n, int q,
                         vector& a,
                         vector& b,
                         int m)
{
    // Sort the input array
    sort(a.begin(), a.end());
 
    // Create segment tree of size 4*n
    vector sum, add, ans;
    sum.assign(n << 2, 0);
    add.assign(n << 2, 0);
 
    // Build the segment tree
    build(sum, a, 1, n, 1);
 
    // Iterate over the queries
    for (int i = 0; i < q; i++) {
        int l = 1, r = n, pos = -1;
        while (l <= r) {
            int m = (l + r) >> 1;
            if (query(sum, add, m, m, 1, n, 1)
                >= b[i]) {
                r = m - 1;
                pos = m;
            }
            else {
                l = m + 1;
            }
        }
        if (pos == -1)
            ans.push_back(0);
        else {
            // Store result in array
            ans.push_back(n - pos + 1);
 
            // Update the elements in
            // the given range
            update(sum, add, pos, n, -m,
                   1, n, 1);
        }
    }
 
    // Print the result of queries
    for (int i = 0; i < ans.size(); i++) {
        cout << ans[i] << " ";
    }
}
 
// Driver Code
int main()
{
    int N = 4;
    int Q = 3;
    int M = 1;
    vector arr = { 1, 2, 3, 4 };
    vector query = { 4, 3, 1 };
 
    // Function Call
    sequenceMaintenance(N, Q, arr, query, M);
    return 0;
}


Java
// Java program for the above approach
import java.io.*;
import java.util.*;
class GFG
{
   
    // Function to build a segment tree
    static void build(Vector sum,Vector a,
                      int l, int r, int rt)
    {
       
        // Check for base case
        if(l == r)
        {
            sum.set(rt, a.get(l - 1));
            return;
        }
       
        // Find mid point
        int m = (l + r) >> 1;
       
        // Recursively build the
        // segment tree
        build(sum, a, l, m, rt << 1);
        build(sum, a, m + 1, r, rt << 1 | 1);
         
    }
   
    // Function for push down operation
    // on the segment tree
    static void pushDown(Vector sum,
                         Vector add,
                         int rt, int ln, int rn)
    {
        if(add.get(rt) != 0)
        {
            add.set(rt << 1, add.get(rt));
            add.set(rt << 1 | 1, add.get(rt));
            sum.set(rt << 1, sum.get(rt << 1) + add.get(rt) * ln);
            sum.set(rt << 1 | 1, sum.get(rt << 1 | 1) + add.get(rt) * rn);
            add.set(rt, 0);
        }
    }
   
    // Function to update the segment tree
    static void update(Vector sum,
                       Vector add,int L,
                       int R, int C, int l, int r, int rt)
    {
       
        // Complete overlap
        if(L <= l && r <= R)
        {
            sum.set(rt,sum.get(rt) + C * (r - l + 1));
            add.set(rt,add.get(rt) + C);
            return;
        }
       
        // Find mid
        int m = (l + r) >> 1;
       
        // Perform push down operation
        // on segment tree
        pushDown(sum, add, rt, m - l + 1, r - m);
       
        // Recursively update the segment tree
        if(L <= m)
        {
            update(sum, add, L, R, C, l, m, rt << 1);
             
        }
        if(R > m)
        {
            update(sum, add, L, R, C, m + 1, r, rt << 1 | 1);
        }
    }
    // Function to process the query
    static int query(Vector sum,Vector add,
                     int L, int R, int l,int r, int rt)
    {
        // Base case
        if (L <= l && r <= R)
        {
            return sum.get(rt);
        }
       
        // Find mid
        int m = (l + r) >> 1;
       
        // Perform push down operation
        // on segment tree
        pushDown(sum, add, rt, m - l + 1, r - m);
        int ans = 0;
       
        // Recursively calculate the result
        // of the query
        if(L <= m)
        {
            ans += query(sum, add, L, R, l, m, rt << 1);
             
        }
        if(R > m)
        {
            ans += query(sum, add, L, R, m + 1, r,rt << 1 | 1);
        }
       
        // Return the result
        return ans;
    }
   
    // Function to count the numbers
    // which are greater than the given query
    static void sequenceMaintenance(int n, int q,
                                    Vector a,
                                    Vector b,int m)
    {
        // Sort the input array
        Collections.sort(a);
       
        // Create segment tree of size 4*n
        Vector sum = new Vector();
        Vector ad = new Vector();
        Vector ans = new Vector();
        for(int i = 0; i < (n << 2); i++)
        {
            sum.add(0);
            ad.add(0);
        }
       
        // Build the segment tree
        build(sum, a, 1, n, 1);
       
        // Iterate over the queries
        for(int i = 0; i < q; i++)
        {
            int l = 1, r = n, pos = -1;
            while(l <= r)
            {
                m = (l + r) >> 1;
                if(query(sum, ad, m, m, 1, n, 1) >= b.get(i))
                {
                    r = m - 1;
                    pos = m;
                }
                else
                {
                    l = m + 1;
                }
            }
            if(pos == -1)
            {
                ans.add(0);
            }
            else
            {
               
                // Store result in array
                ans.add(n - pos + 1);
               
                // Update the elements in
                // the given range
                update(sum, ad, pos, n, -m, 1, n, 1);
            }
        }
       
         // Print the result of queries
        for(int i = 0; i < ans.size(); i++)
        {
            System.out.print(ans.get(i) + " ");
        }
    }
   
    // Driver Code
    public static void main (String[] args)
    {
        int N = 4;
        int Q = 3;
        int M = 1;
        Vector arr = new Vector();
        arr.add(1);
        arr.add(2);
        arr.add(3);
        arr.add(4);
        Vector query = new Vector();
        query.add(4);
        query.add(3);
        query.add(1);
       
        // Function call
        sequenceMaintenance(N, Q, arr, query, M);
    }
}
 
// This code is contributed by rag2127


Python3
# Python3 program for the above approach
 
# Function to build a segment tree
def build(sum, a, l, r, rt):
 
    # Check for base case
    if (l == r):
        sum[rt] = a[l - 1]
        return
         
    # Find mid point
    m = (l + r) >> 1
 
    # Recursively build the
    # segment tree
    build(sum, a, l, m, rt << 1)
    build(sum, a, m + 1, r, rt << 1 | 1)
 
# Function for push down operation
# on the segment tree
def pushDown(sum, add, rt, ln, rn):
 
    if (add[rt]):
        add[rt << 1] += add[rt]
        add[rt << 1 | 1] += add[rt]
        sum[rt << 1] += add[rt] * ln
        sum[rt << 1 | 1] += add[rt] * rn
        add[rt] = 0
 
# Function to update the segment tree
def update(sum, add, L, R, C, l, r, rt):
 
    # Complete overlap
    if (L <= l and r <= R):
        sum[rt] += C * (r - l + 1)
        add[rt] += C
        return
 
    # Find mid
    m = (l + r) >> 1
 
    # Perform push down operation
    # on segment tree
    pushDown(sum, add, rt, m - l + 1, r - m)
 
    # Recursively update the segment tree
    if (L <= m):
        update(sum, add, L, R, C, l,
               m, rt << 1)
 
    if (R > m):
        update(sum, add, L, R, C, m + 1,
               r, rt << 1 | 1)
 
# Function to process the queryy
def queryy(sum, add, L, R, l, r, rt):
 
    # Base case
    if (L <= l and r <= R):
        return sum[rt]
 
    # Find mid
    m = (l + r) >> 1
 
    # Perform push down operation
    # on segment tree
    pushDown(sum, add, rt, m - l + 1, r - m)
 
    ans = 0
 
    # Recursively calculate the result
    # of the queryy
    if (L <= m):
        ans += queryy(sum, add, L, R, l,
                      m, rt << 1)
    if (R > m):
        ans += queryy(sum, add, L, R, m + 1,
                      r, (rt << 1 | 1))
 
    # Return the result
    return ans
 
# Function to count the numbers
# which are greater than the given queryy
def sequenceMaintenance(n, q, a, b, m):
 
    # Sort the input array
    a = sorted(a)
 
    # Create segment tree of size 4*n
    # vector sum, add, ans
    sum = [0] * (4 * n)
    add = [0] * (4 * n)
    ans = []
 
    # Build the segment tree
    build(sum, a, 1, n, 1)
 
    #print(sum)
 
    # Iterate over the queries
    for i in range(q):
        l = 1
        r = n
        pos = -1
         
        while (l <= r):
            m = (l + r) >> 1
            if (queryy(sum, add, m, m,
                       1, n, 1) >= b[i]):
                r = m - 1
                pos = m
 
            else:
                l = m + 1
 
        if (pos == -1):
            ans.append(0)
        else:
             
            # Store result in array
            ans.append(n - pos + 1)
 
            # Update the elements in
            # the given range
            update(sum, add, pos, n,
                   -m, 1, n, 1)
 
    # Print the result of queries
    for i in ans:
        print(i, end = " ")
 
# Driver Code
if __name__ == '__main__':
 
    N = 4
    Q = 3
    M = 1
     
    arr = [ 1, 2, 3, 4 ]
    query = [ 4, 3, 1 ]
 
    # Function call
    sequenceMaintenance(N, Q, arr, query, M)
 
# This code is contributed by mohit kumar 29


C#
// C# program for the above approach
using System;
using System.Collections;
using System.Collections.Generic; 
  
class GFG{
    
// Function to build a segment tree
static void build(ArrayList sum,
                  ArrayList a,
                  int l, int r,
                  int rt)
{
     
    // Check for base case
    if (l == r)
    {
        sum[rt] = a[l - 1];
        return;
    }
   
    // Find mid point
    int m = (l + r) >> 1;
   
    // Recursively build the
    // segment tree
    build(sum, a, l, m, rt << 1);
    build(sum, a, m + 1, r, rt << 1 | 1);
}
   
// Function for push down operation
// on the segment tree
static void pushDown(ArrayList sum,
                     ArrayList add,
                     int rt, int ln,
                     int rn)
{
    if ((int)add[rt] != 0)
    {
        add[rt << 1] = (int)add[rt << 1] +
                       (int)add[rt];
        add[rt << 1 | 1] = (int)add[rt << 1 | 1] +
                           (int)add[rt];
        sum[rt << 1] = (int)sum[rt << 1] +
                       (int)add[rt] * ln;
        sum[rt << 1 | 1] = (int)sum[rt << 1 | 1] +
                           (int)add[rt] * rn;
        add[rt] = 0;
    }
}
   
// Function to update the segment tree
static void update(ArrayList sum,
                   ArrayList add,
                   int L, int R, int C,
                   int l, int r, int rt)
{
     
    // Complete overlap
    if (L <= l && r <= R)
    {
        sum[rt] = (int)sum[rt] +
                    C * (r - l + 1);
        add[rt] = (int)add[rt] + C;
        return;
    }
   
    // Find mid
    int m = (l + r) >> 1;
   
    // Perform push down operation
    // on segment tree
    pushDown(sum, add, rt, m - l + 1,
                           r - m);
   
    // Recursively update the segment tree
    if (L <= m)
        update(sum, add, L, R, C, l, m,
               rt << 1);
    if (R > m)
        update(sum, add, L, R, C, m + 1, r,
               rt << 1 | 1);
}
   
// Function to process the query
static int query(ArrayList sum,
                 ArrayList add,
                 int L, int R, int l,
                 int r, int rt)
{
     
    // Base case
    if (L <= l && r <= R)
    {
        return (int)sum[rt];
    }
   
    // Find mid
    int m = (l + r) >> 1;
   
    // Perform push down operation
    // on segment tree
    pushDown(sum, add, rt, m - l + 1,
                           r - m);
   
    int ans = 0;
   
    // Recursively calculate the result
    // of the query
    if (L <= m)
        ans += query(sum, add, L, R, l, m,
                     rt << 1);
    if (R > m)
        ans += query(sum, add, L, R, m + 1, r,
                     rt << 1 | 1);
   
    // Return the result
    return ans;
}
   
// Function to count the numbers
// which are greater than the given query
static void sequenceMaintenance(int n, int q,
                                ArrayList a,
                                ArrayList b,
                                int m)
{
     
    // Sort the input array
    a.Sort();
   
    // Create segment tree of size 4*n
    ArrayList sum = new ArrayList();
    ArrayList add = new ArrayList();
    ArrayList ans = new ArrayList();
      
    for(int i = 0; i < (n << 2); i++)
    {
        sum.Add(0);
        add.Add(0);
    }
      
    // Build the segment tree
    build(sum, a, 1, n, 1);
   
    // Iterate over the queries
    for(int i = 0; i < q; i++)
    {
        int l = 1, r = n, pos = -1;
        while (l <= r)
        {
            m = (l + r) >> 1;
            if (query(sum, add, m, m,
                      1, n, 1) >= (int)b[i])
            {
                r = m - 1;
                pos = m;
            }
            else
            {
                l = m + 1;
            }
        }
        if (pos == -1)
            ans.Add(0);
        else
        {
             
            // Store result in array
            ans.Add(n - pos + 1);
   
            // Update the elements in
            // the given range
            update(sum, add, pos, n, -m,
                   1, n, 1);
        }
    }
   
    // Print the result of queries
    for(int i = 0; i < ans.Count; i++)
    {
        Console.Write(ans[i] + " ");
    }
}
  
// Driver Code
public static void Main(string[] args)
{
    int N = 4;
    int Q = 3;
    int M = 1;
     
    ArrayList arr = new ArrayList(){ 1, 2, 3, 4 };
    ArrayList query = new ArrayList(){ 4, 3, 1 };
   
    // Function call
    sequenceMaintenance(N, Q, arr, query, M);
}
}
 
// This code is contributed by rutvik_56


输出:
1 2 4

时间复杂度: O(N +(Q * logN))
辅助空间: O(N)