给定大小为N , Q ,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]为查询数组。
- 对所有查询进行排序,将L值从0到√n– 1的查询放在一起,然后将所有从√n到2×√n– 1的查询放在一起,依此类推。块中的所有查询均按R值的升序排序。
- 以每个查询都使用上一个查询中计算出的结果的方式,一个接一个地处理所有查询。
- 我们将维护频率数组,该数组将对出现在[L,R]范围内的arr [i]的频率进行计数。
- 例如: arr [] = [3、4、6、2、7、1],L = 0,R = 4和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)。