【问题标题】:Mo's algorithm to compute "power" of array莫的算法来计算阵列的“权力”
【发布时间】:2016-04-04 14:36:01
【问题描述】:

最近,我学习了Mo's algorithm 用于查询的平方根分解,以加快解决某些问题的速度。

为了练习实现,我一直在尝试使用这个想法解决D. Powerful array(过去在 Codeforces 上的竞赛问题)。问题如下:

您有一个包含 整数 的数组。

考虑一个任意子数组。将 定义为该子数组中整数 的出现次数。子数组的定义为所有整数 之和(请注意,只有正数项不为零)。

回答 查询。在每个查询中,给定两个整数,计算

它持有:



使用 Mo 的算法,我在 中编写了离线解决此问题的代码。我确信这个问题可以使用这个算法和时间复杂度来解决,因为我检查了其他人接受的代码,他们也使用了类似的算法。

不过,我的代码是gets a time limit exceeded verdict

下面是我写的代码:

#include <ios>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
#include <utility>
#include <map>

int sqt;
long long int ans = 0;
long long int arr[200005] = {};
long long int cnt[1000005] = {};
long long int tans[200005] = {};

struct el
{
    int l, r, in;
};

bool cmp(const el &x, const el &y)
{
    if (x.l/sqt != y.l/sqt)
        return x.l/sqt < y.l/sqt;
    return x.r < y.r;
}

el qr[200005];

int main()
{
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);
    std::cout.tie(NULL);
    int n, q, a, b;
    std::cin >> n >> q;
    sqt = sqrt((double)(n))+27;
    for (int i = 0; i < n; i++)
        std::cin >> arr[i];
    for (int i = 0; i < q; i++)
    {
        std::cin >> a >> b;
        a--; b--;
        qr[i].l = a;
        qr[i].r = b;
        qr[i].in = i;
    }
    std::sort(qr, qr+q, cmp);

    int li = 0; //left iterator
    int ri = 0; //right iterator
    ans = arr[0];
    cnt[arr[0]]++;

    for (int i = 0; i < q; i++)
    {
        while (li < qr[i].l)
        {
            ans -= cnt[arr[li]]*cnt[arr[li]]*arr[li];
            cnt[arr[li]]--;
            ans += cnt[arr[li]]*cnt[arr[li]]*arr[li];
            li++;
        }
        while (li > qr[i].l)
        {
            li--;
            ans -= cnt[arr[li]]*cnt[arr[li]]*arr[li];
            cnt[arr[li]]++;
            ans += cnt[arr[li]]*cnt[arr[li]]*arr[li];
        }
        while (ri < qr[i].r)
        {
            ri++;
            ans -= cnt[arr[ri]]*cnt[arr[ri]]*arr[ri];
            cnt[arr[ri]]++;
            ans += cnt[arr[ri]]*cnt[arr[ri]]*arr[ri];
        }
        while (ri > qr[i].r)
        {
            ans -= cnt[arr[ri]]*cnt[arr[ri]]*arr[ri];
            cnt[arr[ri]]--;
            ans += cnt[arr[ri]]*cnt[arr[ri]]*arr[ri];
            ri--;
        }
        tans[qr[i].in] = ans;
    }
    for (int i = 0; i < q; i++)
        std::cout << tans[i] << '\n';
}

您能否提出任何非渐近(甚至可能是渐近)改进,以使程序足够快以通过时间限制?

我已经尝试了以下方法,但无济于事:

  1. 使用向量而不是数组。
  2. 使用两个嵌套对而不是结构。
  3. 仅使用一对,然后使用地图尝试恢复正确的答案顺序。
  4. sqt 中添加一些不同的常量(如上面代码中的)。
  5. 在结构el 本身中重载

我觉得我错过了一些重要的东西,因为我检查过的其他代码似乎以相当大的余地(大约半秒)通过了时间限制。然而,他们似乎使用与我的代码相同的算法。

任何帮助将不胜感激!

【问题讨论】:

  • 您根据 a、l、r 和 s 定义子数组的幂,但是每个查询只指定了 a、l 和 r。总和是否超过所有 s?
  • 是的,它是所有 s 的总和。我已将此添加到问题中,感谢您的澄清!
  • 您提供的描述链接blog.anudeep2011.com/mos-algorithm 在底部说,“注意:该代码在提交时会给出 TLE,如果添加了快速 I/O,它将给出 AC。删除了快速 I/O 到保持代码干净。”似乎 IO 可能是问题所在,因为 cin/cout 不是很快。我建议检查 IO 时间与计算时间,看看 IO 是否可能是超出时间限制的原因。

标签: c++ algorithm data-structures


【解决方案1】:

你可以降低强度

    while (li < qr[i].l)
    {
        ans -= cnt[arr[li]]*cnt[arr[li]]*arr[li];
        cnt[arr[li]]--;
        ans += cnt[arr[li]]*cnt[arr[li]]*arr[li];
        li++;
    }

    while (li < qr[i].l)
    {
        ans -= (2*cnt[arr[li]]-1)*arr[li];
        cnt[arr[li]]--;
        li++;
    }

对其他人也是如此。

【讨论】:

  • 似乎改变它可以让我的代码被接受,虽然只是勉强(4960 毫秒)。
【解决方案2】:

您可以修改MO的排序函数比较函数cmp

您的版本:

bool cmp(const el &x, const el &y)
{
    if (x.l/sqt != y.l/sqt)
        return x.l/sqt < y.l/sqt;
    return x.r < y.r;
}

优化:

如果块是偶数,可以对R进行降序排列,如果块是奇数,可以 >按升序对 R 进行排序。当从一个块移动到另一个块时,这将大大减少 R 指针的移动。

我的代码:

bool cmp(const el &x, const el &y)
{
    if (x.l/sqt != y.l/sqt)
        return x.l/sqt < y.l/sqt;
    return (x.l/sqt & 1) ?  x.r < y.r : x.r > y.r; // avoids TLE
}

【讨论】:

    猜你喜欢
    • 2015-01-16
    • 2017-07-03
    • 1970-01-01
    • 2013-04-14
    • 1970-01-01
    • 2021-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多