MO的算法用于查找两个数组中存在的元素数量

时间:2016-12-07 05:10:10

标签: c++ arrays algorithm sub-array

我有 2 数组,$user = User::with([ 'products' => function ($query) use ($date, $order) { $query->where(function ($q) use ($date, $order) { $q->where('created_at', $date); //This can accept more where() condition })->orderBy($order,'DESC'); }])->get(); 1 已编入索引)和before[N+1]after[]的子数组)。现在,对于 M 查询,我需要查找before[]after[]对于给定范围before[]有多少元素。

例如:
N = 5
之前:(2,1,3,4,5)
之后:(1,3,4,5)
M = 2
在[1]之前和[5]之前,在[]之后存在L = 1,R = 5→4个元素(1,3,4,5) 在[2]之前和[4]之前,[]之后存在L = 2,R = 4→3元素(1,3,4)

我正在尝试使用MO的算法来找到它。以下是我的代码:

l,r

现在问题是我无法弄清楚如何定义using namespace std; int N, Q; // Variables, that hold current "state" of computation long long current_answer; long long cnt[100500]; // Array to store answers (because the order we achieve them is messed up) long long answers[100500]; int BLOCK_SIZE; // We will represent each query as three numbers: L, R, idx. Idx is // the position (in original order) of this query. pair< pair<int, int>, int> queries[100500]; // Essential part of Mo's algorithm: comparator, which we will // use with std::sort. It is a function, which must return True // if query x must come earlier than query y, and False otherwise. inline bool mo_cmp(const pair< pair<int, int>, int> &x, const pair< pair<int, int>, int> &y) { int block_x = x.first.first / BLOCK_SIZE; int block_y = y.first.first / BLOCK_SIZE; if(block_x != block_y) return block_x < block_y; return x.first.second < y.first.second; } // When adding a number, we first nullify it's effect on current // answer, then update cnt array, then account for it's effect again. inline void add(int x) { current_answer -= cnt[x] * cnt[x] * x; cnt[x]++; current_answer += cnt[x] * cnt[x] * x; } // Removing is much like adding. inline void remove(int x) { current_answer -= cnt[x] * cnt[x] * x; cnt[x]--; current_answer += cnt[x] * cnt[x] * x; } int main() { cin.sync_with_stdio(false); cin >> N >> Q; // Q- number of queries BLOCK_SIZE = static_cast<int>(sqrt(N)); long long int before[N+1]; // 1 indexed long long int after[] // subarray // Read input queries, which are 0-indexed. Store each query's // original position. We will use it when printing answer. for(long long int i = 0; i < Q; i++) { cin >> queries[i].first.first >> queries[i].first.second; queries[i].second = i; } // Sort queries using Mo's special comparator we defined. sort(queries, queries + Q, mo_cmp); // Set up current segment [mo_left, mo_right]. int mo_left = 0, mo_right = -1; for(long long int i = 0; i < Q; i++) { // [left, right] is what query we must answer now. int left = queries[i].first.first; int right = queries[i].first.second; // Usual part of applying Mo's algorithm: moving mo_left // and mo_right. while(mo_right < right) { mo_right++; add(after[mo_right]); } while(mo_right > right) { remove(after[mo_right]); mo_right--; } while(mo_left < left) { remove(after[mo_left]); mo_left++; } while(mo_left > left) { mo_left--; add(after[mo_left]); } // Store the answer into required position. answers[queries[i].second] = current_answer; } // We output answers *after* we process all queries. for(long long int i = 0; i < Q; i++) cout << answers[i] << "\n"; add function

有人可以帮我解决这些问题吗?

1 个答案:

答案 0 :(得分:0)

注意:我将给定的数组表示为ab

  1. 让我们学习如何添加一个新位置(向右移动一个)。如果a[r]已经存在,您可以忽略它。否则,我们需要添加a[r]并在b[r]中将a的出现次数添加到答案中。最后,如果b[r]已经在a,我们需要在答案中添加一个。请注意,我们需要对数组进行两次计数:一个用于第一个数组,另一个用于第二个数组。

  2. 我们知道如何在O(1)中添加一个位置,因此我们几乎就在那里。我们如何处理删除?

  3. 我们假设我们要删除子段。我们可以轻松修改计数数组。但是我们如何恢复答案呢?好吧,我们不是。你的解决方案是这样的:

    • 保存当前答案
    • 添加子细分
    • 回答查询
    • 删除它(我们关注计数数组并忽略答案)
    • 恢复已保存的答案
  4. 那就是它。当我们将左指针移动到下一个块时,它需要重建结构,但在最坏的情况下仍需要O(N sqrt(N))时间。

    注意:当我们移除一个位置时,可能会使用count数组直接重新计算答案,但我上面展示的方式看起来也更容易。