【问题标题】:MO's Algorithm to find number of elements present in both arrayMO的算法来查找两个数组中存在的元素数量
【发布时间】:2017-04-21 22:43:37
【问题描述】:

我有 2 个数组,before[N+1]1 索引)和 after[]before[] 的子数组)。现在对于 M 查询,我需要找出给定范围 l,rbefore[] 中有多少 after[] 元素。

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

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

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 functionremove function

有人可以帮我解决这些功能吗?

【问题讨论】:

  • before[] 中的元素是唯一的吗?如果是,为什么不做一个布尔数组mark[],其中mark[i]表示before[i]是否出现在after[]中?然后查询 (l, r) 变为在 mark[l] 和 mark[r] 之间计数 true。
  • 如果after[]before[]子数组,那么NumberOfOccurrence(NOC)after[] 中永远不能超过NumberOfElements(An)NOC=max(r-l+1, An)
  • @MoTao 不,before[] 中的元素不是唯一的。
  • @sameerkn 我不认为这个公式适用于所有情况。

标签: c++ arrays algorithm sub-array


【解决方案1】:

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

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

  2. 我们知道如何在O(1) 中添加一个位置,所以我们快到了。我们如何处理删除?

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

    • 保存当前答案
    • 添加子段
    • 回答问题
    • 删除它(我们关心计数数组并忽略答案)
    • 恢复保存的答案

就是这样。当我们将左指针移动到下一个块时需要重建结构,但在最坏的情况下仍然需要O(N sqrt(N))时间。

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

【讨论】:

  • 感谢您的解释。如何使用代码实现这一点?你能帮我写代码吗?我不知道添加和删除的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-19
  • 2011-02-27
  • 2013-10-12
  • 1970-01-01
  • 2020-03-26
  • 2014-04-19
相关资源
最近更新 更多