📌  相关文章
📜  范围查询以计算位于给定范围内的元素:MO的算法

📅  最后修改于: 2021-05-04 19:15:24             🧑  作者: Mango

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

例子:

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

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

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

    下面是上述方法的实现:

    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[] = { 2, 0, 3, 1, 4, 2, 5, 11 };
        int N = sizeof(arr) / sizeof(arr[0]);
      
        int A = 1, B = 5;
        blk_sz = sqrt(N);
        Query Q[] = { { 0, 2 }, { 0, 3 }, { 5, 7 } };
      
        int M = sizeof(Q) / sizeof(Q[0]);
      
        // Answer the queries
        queryResults(arr, N, Q, M, A, B);
        return 0;
    }


    输出:
    2
    3
    2