给定一个大小为N的数组 arr和形式为 L、R 和 X 的 Q查询,任务是打印由 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]为查询数组。
- 对所有查询进行排序,将 L 值从0 到 √n – 1的查询放在一起,然后将所有查询从√n 到 2×√n – 1 ,依此类推。块内的所有查询都按 R 值的升序排序。
- 以每个查询都使用在前一个查询中计算出的结果的方式一个一个地处理所有查询。
- 我们将维护频率数组,该数组将计算 arr[i] 出现在 [L, R] 范围内的频率。
- 例如: arr[]=[3, 4, 6, 2, 7, 1], L=0, R=4 and X=5
Initially frequency array is initialized to 0 i.e freq[]=[0….0]
Step 1– add arr[0] and increment its frequency as freq[arr[0]]++ i.e freq[3]++
and freq[]=[0, 0, 0, 1, 0, 0, 0, 0]Step 2– Add arr[1] and increment freq[arr[1]]++ i.e freq[4]++
and freq[]=[0, 0, 0, 1, 1, 0, 0, 0]Step 3– Add arr[2] and increment freq[arr[2]]++ i.e freq[6]++
and freq[]=[0, 0, 0, 1, 1, 0, 1, 0]Step 4– Add arr[3] and increment freq[arr[3]]++ i.e freq[2]++
and freq[]=[0, 0, 1, 1, 1, 0, 1, 0]Step 5– Add arr[4] and increment freq[arr[4]]++ i.e freq[7]++
and freq[]=[0, 0, 1, 1, 1, 0, 1, 1]Step 6– Now we need to find the numbers of elements less than or equal to X(here X=5).
Step 7– The answer is equal to
To calculate the sum in step 7, we cannot do iteration because that would lead to O(N) time complexity per query so we will use sqrt decomposition technique to find the sum whose time complexity is O(√n) per query.
// 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)。
如果您想与行业专家一起参加直播课程,请参阅Geeks Classes Live