📌  相关文章
📜  使用MO算法查询值在A到B范围内的元素

📅  最后修改于: 2021-05-17 23:02:02             🧑  作者: Mango

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

给定一个由N个元素和两个整数AB组成的数组arr [] ,任务是回答Q个查询,每个查询具有两个整数LR。对于每个查询,请找到子数组arr [L,R]中的元素数量,其中位于AB (含)范围内。

例子:

方法:想法是使用MO的算法对所有查询进行预处理,以便一个查询的结果可以在下一个查询中使用。下面是步骤说明:

  1. 将查询分组为多个块,其中每个块包含(0到√N– 1),(√N到2x√N– 1)的起始范围值,依此类推。以R的升序对块内的查询进行排序。
  2. 以每个查询都使用上一个查询中计算出的结果的方式,一个接一个地处理所有查询。
  3. 保持频率数组,该数组将对出现在[L,R]范围内的arr [i]的频率进行计数。

例如:

要在步骤7中计算总和,我们无法进行迭代,因为这将导致每个查询的时间复杂度为O(N)。因此,我们将使用平方根分解技术来找到总和,每个查询的时间复杂度为O(√N)。

下面是上述方法的实现:

C++
// C++ implementation to find the
// values in the range A to B
// in a subarray of L to R
  
#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;
};
  
// 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 A, int B)
{
    int ans = 0;
    int left_blk, right_blk;
    left_blk = getblocknumber(A);
    right_blk = getblocknumber(B);
  
    // If left block is equal to rigth block
    // then we can traverse that block
    if (left_blk == right_blk) {
        for (int i = A; i <= B; i++)
            ans += frequency[i];
    }
    else {
        // Traversing first block in
        // range
        for (int i = A;
             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 <= B; 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,
                  int A, int B)
{
  
    // 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;
  
        // 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("%d\n", getans(A, B));
    }
}
  
// Driver code
int main()
{
  
    int arr[] = { 3, 4, 6, 2, 7, 1 };
    int N = sizeof(arr) / sizeof(arr[0]);
  
    int A = 1, B = 6;
    blk_sz = sqrt(N);
    Query Q[] = { { 0, 4 } };
  
    int M = sizeof(Q) / sizeof(Q[0]);
  
    // Answer the queries
    queryResults(arr, N, Q, M, A, B);
  
    return 0;
}


输出:
4

时间复杂度: O(Q *√N)
空间复杂度: O(N)