【问题标题】:Number of occurrences of each distinct integer in given ranges for an array数组的给定范围内每个不同整数的出现次数
【发布时间】:2019-06-14 20:53:39
【问题描述】:

给定一个包含 n 个整数的数组(n <= 1e6) [a0, a1, a2, ... an-1] (a[i] <= 1e9) 和多个查询。在每个查询 2 整数 lr (0 <= l <= r <= n-1) 中,我们需要返回此范围内每个不同整数的计数(lr 包括在内)。

我只能想出一个蛮力解决方案来遍历每个查询的完整范围。

d={}
for i in range(l, r+1):
    if arr[i] not in d:
        d[arr[i]]=0
    d[arr[i]]+=1

例如:

Array is [1, 1, 2, 3, 1, 2, 1]

Query 1: l=0, r=6, Output: 4, 2, 3 (4 for 4 1's, 2, for 2 2's and 1 for 1 3)
Query 2: l=3, r=5, Output: 1, 1, 1

编辑-我想出了类似的东西,但它的复杂性仍然很高。我想是因为那个插入操作。

const ll N = 1e6+5;
ll arr[N];
unordered_map< ll, ll > tree[4 * N];
int n, q;

void build (ll node = 1, ll start = 1, ll end = n) {
    if (start == end) {
        tree[node][arr[start]] = 1;
        return;
    }
    ll mid = (start + end) / 2;
    build (2 * node, start, mid);
    build (2 * node + 1, mid + 1, end);
    for (auto& p : tree[2 * node]) {
        ll x = p.ff;
        ll y = p.ss;
        tree[node][x] += y;
    }
    for (auto& p : tree[2 * node + 1]) {
        ll x = p.ff;
        ll y = p.ss;
        tree[node][x] += y;
    }
}

vector< ll > query (ll node, ll l, ll r, ll start = 1, ll end = n) {
    vector< ll > ans;
    if (end < l or start > r) return ans;
    if (start >= l and end <= r) {
        for (auto p : tree[node]) {
            ans.push_back (p.ss);
        }
        return ans;
    }
    ll mid = (start + end) / 2;
    vector< ll > b = query (2 * node, l, r, start, mid);
    ans.insert (ans.end (), b.begin (), b.end ());
    b = query (2 * node + 1, l, r, mid + 1, end);
    ans.insert (ans.end (), b.begin (), b.end ());
    return ans;
}

【问题讨论】:

  • 你试过使用分段树还是二叉索引树?
  • 有原始问题的链接吗?
  • 分别是1e6和1e9、10e6和10e9吗?
  • 多少查询?

标签: algorithm segment-tree


【解决方案1】:

您可以按照here 的描述使用二叉索引树。与其将范围总和存储在节点中,不如存储从值到相应范围的计数的映射。

现在查询带有输入x 的树,以找到表示相应索引前缀[1..i] 中每个元素出现频率的映射。这将需要合并 O(log n) 映射。

现在您可以执行两个查询:一个查询l-1,另一个查询r。从后者中“减去”前者的结果图。地图减法是逐项的。我会让你弄清楚细节。`

每个查询的时间为 O(k log n),其中 k 是地图大小。这最多是输入数组中不同元素的数量。

【讨论】:

    【解决方案2】:

    听起来这可能是我们如何安排查询的候选者。假设查询的数量和输入的长度都在n的顺序上,类似于this post,我们可以根据floor(l / sqrt(n))对它们进行分桶,并按r对每个桶进行排序。现在我们有了sqrt(n) 存储桶。

    每个桶的q 查询最多会因l 中的每次移动而发生O(q * sqrt(n)) 变化,并且由于r 的逐渐变化而最多O(n) 变化(因为我们按@987654333 对每个桶进行排序@,间隔的那一侧只会在我们处理存储桶时稳步增加)。

    处理右侧的更改一个桶中的所有间隔绑定在O(n),我们有sqrt(n)桶,所以右侧是O(n * sqrt(n)。并且由于所有qs的数量是O(n)(假设),并且每个人最多需要左侧更改O(sqrt(n)),因此左侧的更改也是O(n * sqrt(n))

    因此,总时间复杂度为O(n * sqrt(n) + k),其中k 是输出的总数。 (更新后的数据结构可以是一个哈希图,也允许在其当前存储上进行迭代。)

    【讨论】:

    • 可以在线回答问题吗?正如codeforces.com/problemset/problem/86/D,这个问题已在代码部队中提出,所有解决方案几乎都按照您的意思进行。
    • @FarhanTahir 我不明白我们如何将这种方法用于在线查询。但是,codeforces 问题不是提前呈现所有查询吗? (有讨论here。)
    • 是的,但我需要在线解决问题。现在我正在尝试构建一个以 unordered_map 作为节点的段树。我猜它可能超过了内存限制。
    • 我用段树代码编辑了问题。你能想到什么可以通过使用惰性传播来降低其复杂性吗?
    • @FarhanTahir 我会在问题描述中添加您需要在线回答查询的内容。这似乎是一个重要的细节。
    【解决方案3】:

    您可以使用哈希映射。从 l 迭代到 r 并将每个元素存储为键,并将出现次数存储为计数。将需要 O(n) 来指定给定范围内不同元素计数的数量。每次将元素插入哈希映射时,都必须检查哈希映射中是否存在元素。如果元素已经存在,则更新计数,否则保持计数为 1。

    【讨论】:

    • 你觉得我先写的python代码做了什么?
    • 你按我说的做了,抱歉没仔细看。我会尝试找到更好的方法。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2020-11-09
    • 1970-01-01
    • 2014-04-21
    • 2012-06-26
    • 2015-12-13
    • 2017-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多