先决条件: MO 算法,SQRT 分解
给定一个包含N 个元素的数组 arr[]和两个整数A到B ,任务是回答 Q 个查询,每个查询都有两个整数L和R。对于每个查询,找到子数组 arr[L, R]中元素的数量,其中位于 A到B (含)范围内。
例子:
Input: arr[] = {3, 4, 6, 2, 7, 1}, A = 1, B = 6, query = {0, 4}
Output: 4
Explanation:
All 3, 4, 6, 2 lies within 1 to 6 in the subarray {3, 4, 6, 2}
Therefore, the count of such elements is 4.
Input: arr[] = {0, 1, 2, 3, 4, 5, 6, 7}, A = 1, B = 5, query = {3, 5}
Output: 3
Explanation:
All the elements 3, 4 and 5 lies within the range 1 to 5 in the subarray {3, 4, 5}.
Therefore, the count of such elements is 3.
方法:思想是使用MO的算法对所有查询进行预处理,以便一个查询的结果可以在下一个查询中使用。下面是步骤的图示:
- 将查询分组为多个块,其中每个块包含 (0 到 √N – 1)、(√N 到 2x√N – 1) 等的起始范围的值。按 R 的递增顺序对块内的查询进行排序。
- 以每个查询都使用在前一个查询中计算出的结果的方式一个一个地处理所有查询。
- 维护频率数组,该数组将计算 arr[i] 出现在 [L, R] 范围内的频率。
例如:
arr[] = [3, 4, 6, 2, 7, 1], L = 0, R = 4 and A = 1, B = 6
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 between A and B.
Step 7: The answer is equal to
为了计算第 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)
如果您想与行业专家一起参加直播课程,请参阅Geeks Classes Live