📌  相关文章
📜  子数组中小于或等于数字的元素数:MO的算法

📅  最后修改于: 2021-04-23 22:02:32             🧑  作者: Mango

给定大小为NQ ,L,R和X的查询的数组arr ,任务是在L到R表示的子数组中打印小于或等于X的元素数。

先决条件: MO的算法,Sqrt分解

例子:

Input: 
arr[] = {2, 3, 4, 5}
Q = {{0, 3, 5}, {0, 2, 2}}
Output:
 4
 1
Explanation:
Number of elements less than or equal to 5
in arr[0..3] is 4 (all elements)

Number of elements less than or equal to 2 
in arr[0..2] is 1 (only 2)

方法:
MO的算法的思想是对所有查询进行预处理,以便一个查询的结果可以在下一个查询中使用。
假设arr [0…n-1]为输入数组,而Q [0..m-1]为查询数组。

  1. 对所有查询进行排序,将L值从0到√n– 1的查询放在一起,然后将所有从√n到2×√n– 1的查询放在一起,依此类推。块中的所有查询均按R值的升序排序。
  2. 以每个查询都使用上一个查询中计算出的结果的方式,一个接一个地处理所有查询。
  3. 我们将维护频率数组,该数组将对出现在[L,R]范围内的arr [i]的频率进行计数。
  4. 例如: arr [] = [3、4、6、2、7、1],L = 0,R = 4和X = 5
// C++ program to answer queries to 
// count number of elements smaller 
// than or equal to x.
#include 
using namespace std;
  
#define MAX 100001
#define SQRSIZE 400
  
// Variable to represent block size.
// This is made global so compare()
// of sort can use it.
int query_blk_sz;
  
// Structure to represent a
// query range
struct Query {
  
    int L;
    int R;
    int x;
};
  
// Frequency array
// to keep count of elements
int frequency[MAX];
  
// Array which contains the frequency
// of a particular block
int blocks[SQRSIZE];
  
// Block size
int blk_sz;
  
// Function used to sort all queries 
// so that all queries of the same
// block are arranged together and 
// within a block, queries are sorted 
// in increasing order of R values.
bool compare(Query x, Query y)
{
    if (x.L / query_blk_sz != 
        y.L / query_blk_sz)
        return x.L / query_blk_sz < 
               y.L / query_blk_sz;
  
    return x.R < y.R;
}
  
// Function used to get the block
// number of current a[i] i.e ind
int getblocknumber(int ind)
{
    return (ind) / blk_sz;
}
  
// Function to get the answer
// of range [0, k] which uses the
// sqrt decompostion technique
int getans(int k)
{
    int ans = 0;
    int left_blk, right_blk;
    left_blk = 0;
    right_blk = getblocknumber(k);
  
    // If left block is equal to
    // rigth block then we can traverse
    // that block
    if (left_blk == right_blk) {
        for (int i = 0; i <= k; i++)
            ans += frequency[i];
    }
    else {
        // Traversing first block in 
        // range
        for (int i = 0; i < 
             (left_blk + 1) * blk_sz; i++)
            ans += frequency[i];
  
        // Traversing completely overlapped
        // blocks in range
        for (int i = left_blk + 1; 
             i < right_blk; i++)
            ans += blocks[i];
  
        // Traversing last block in range
        for (int i = right_blk * blk_sz;
             i <= k; i++)
            ans += frequency[i];
    }
    return ans;
}
  
void add(int ind, int a[])
{
    // Increment the frequency of a[ind]
    // in the frequency array
    frequency[a[ind]]++;
  
    // Get the block number of a[ind]
    // to update the result in blocks
    int block_num = getblocknumber(a[ind]);
  
    blocks[block_num]++;
}
void remove(int ind, int a[])
{
    // Decrement the frequency of 
    // a[ind] in the frequency array
    frequency[a[ind]]--;
  
    // Get the block number of a[ind]
    // to update the result in blocks
    int block_num = getblocknumber(a[ind]);
  
    blocks[block_num]--;
}
void queryResults(int a[], int n,
                  Query q[], int m)
{
  
    // Initialize the block size
    // for queries
    query_blk_sz = sqrt(m);
  
    // Sort all queries so that queries
    // of same blocks are arranged
    // together.
    sort(q, q + m, compare);
  
    // Initialize current L,
    // current R and current result
    int currL = 0, currR = 0;
  
    for (int i = 0; i < m; i++) {
  
        // L and R values of the 
        // current range
  
        int L = q[i].L, R = q[i].R,
                x = q[i].x;
  
        // Add Elements of current
        // range
        while (currR <= R) {
            add(currR, a);
            currR++;
        }
        while (currL > L) {
            add(currL - 1, a);
            currL--;
        }
  
        // Remove element of previous
        // range
        while (currR > R + 1)
  
        {
            remove(currR - 1, a);
            currR--;
        }
        while (currL < L) {
            remove(currL, a);
            currL++;
        }
        printf("query[%d, %d, %d] : %d\n", 
               L, R, x, getans(x));
    }
}
// Driver code
int main()
{
  
    int arr[] = { 2, 0, 3, 1, 4, 2, 5, 11 };
    int N = sizeof(arr) / sizeof(arr[0]);
  
    blk_sz = sqrt(N);
    Query Q[] = { { 0, 2, 2 }, { 0, 3, 5 },
                { 5, 7, 10 } };
  
    int M = sizeof(Q) / sizeof(Q[0]);
      
    // Answer the queries
    queryResults(arr, N, Q, M);
    return 0;
}
输出:
query[0, 2, 2] : 2
query[0, 3, 5] : 4
query[5, 7, 10] : 2

时间复杂度: O(Q×√N)
MO的算法需要O(Q×√N)时间,而sqrt分解技术需要O(Q×√N)时间才能回答freq [0] +….freq [k]之和,因此总时间复杂度为O( Q×√N+ Q×√N)是O(Q×√N)。