Количество вхождений каждого отдельного целого числа в заданных диапазонах для массива

Дан массив из n целых чисел (n <= 1e6) [a0, a1, a2, ... an-1] (a[i] <= 1e9) и несколько запросов. В каждом запросе задано 2 целых чисел l и r (0 <= l <= r <= n-1), и нам нужно вернуть количество каждого отдельного целого числа в этом диапазоне (l и r включительно).

Я могу только придумать решение грубой силы для перебора всего диапазона для каждого запроса.

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;
}

person Farhan Tahir    schedule 14.06.2019    source источник
comment
Пробовали ли вы использовать дерево сегментов или дерево двоичных индексов?   -  person 0x499602D2    schedule 15.06.2019
comment
Я пытался использовать дерево сегментов, но лучшее, что я могу придумать, это подсчет различных целых чисел в заданном диапазоне, но ничего о подсчете каждого отдельного целого числа в заданном диапазоне.   -  person Farhan Tahir    schedule 15.06.2019
comment
Есть ссылка на исходную проблему?   -  person juvian    schedule 15.06.2019
comment
1e6 и 1e9, 10e6 и 10e9 соответственно?   -  person Marko Cain    schedule 15.06.2019
comment
Сколько запросов?   -  person גלעד ברקן    schedule 15.06.2019
comment
Нет, это 1e6 и 1e9. Запросов может быть до 1e5.   -  person Farhan Tahir    schedule 15.06.2019


Ответы (3)


Вы можете использовать двоичное индексное дерево, как описано здесь. Вместо того, чтобы хранить суммы диапазонов в узлах, храните карты от значений до счетчиков для соответствующих диапазонов.

Теперь запросите дерево с входными данными x, чтобы найти карту для представления частот появления каждого элемента в соответствующем префиксе индекса [1..i]. Это потребует слияния карт O(log n).

Теперь вы можете сделать два запроса: один для l-1, а другой для r. «Вычтите» первую карту результатов из второй. Вычитание карты осуществляется по записи. Я позволю тебе обсудить детали.

Время для каждого запроса будет O (k log n), где k — размер карты. Это будет не более чем количество различных элементов во входном массиве.

person Gene    schedule 15.06.2019

Похоже, это может быть кандидатом на то, как мы организуем запросы. Предполагая, что и количество запросов, и длина ввода имеют порядок n, как и в этом сообщении, мы можем разделить их по floor(l / sqrt(n)) и отсортировать каждое ведро по r. Теперь у нас есть sqrt(n) ведра.

Запросы q каждой корзины будут иметь не более O(q * sqrt(n)) изменений из-за каждого движения в l и не более O(n) изменений из-за постепенного изменения r (поскольку мы отсортировали каждую корзину по r, эта сторона интервала только постоянно увеличивается по мере обработки корзины). ).

Обработка изменений в правой части всех интервалов в одном сегменте привязана к O(n), и у нас есть sqrt(n) сегментов, так что O(n * sqrt(n) для правой стороны. А так как число всех q равно O(n) (предполагается), и каждое из них требует не более O(sqrt(n)) изменений с левой стороны, изменений для левой стороны также O(n * sqrt(n)).

Таким образом, общая временная сложность будет равна O(n * sqrt(n) + k), где k — это общее количество выводимых чисел. (Обновленная структура данных может быть хэш-картой, которая также позволяет выполнять итерации в своем текущем хранилище.)

person גלעד ברקן    schedule 15.06.2019
comment
Можно ли ответить на вопросы онлайн? Поскольку codeforces.com/problemset/problem/86/D этот вопрос был задан в кодовые силы и все решения делают почти то, что вы пытаетесь сказать. - person Farhan Tahir; 15.06.2019
comment
@FarhanTahir Я не понимаю, как мы могли бы использовать этот метод с онлайн-запросами. Но разве проблема codeforces не в том, что все запросы представляются заранее? (Обсуждение здесь.) - person גלעד ברקן; 15.06.2019
comment
Да, но мне нужно решить вопросы онлайн. Сейчас я пытаюсь построить дерево сегментов с узлами unordered_map. Я предполагаю, что это может превышать предел памяти. - person Farhan Tahir; 15.06.2019
comment
Я отредактировал вопрос с кодом дерева сегментов. Можете ли вы придумать что-нибудь, чтобы уменьшить его сложность, используя ленивое распространение? - person Farhan Tahir; 15.06.2019
comment
@FarhanTahir Я бы добавил к описанию вопроса, что вам нужно отвечать на запросы в Интернете. Вроде важная деталь. - person גלעד ברקן; 15.06.2019

Вы можете использовать хэш-карту. выполнять итерацию от l до r и сохранять каждый элемент как ключ, а вхождение как count. Требуется O (n), чтобы указать количество различных элементов в заданном диапазоне. Вы должны проверять наличие или отсутствие элемента в хэш-карте каждый раз, когда вы вставляете элемент в хэш-карту. Если элемент уже существует, обновите счетчик, иначе оставьте счетчик равным 1.

person NITESH KUMAR    schedule 15.06.2019
comment
Как вы думаете, что я сделал в коде Python, который написал первым? - person Farhan Tahir; 15.06.2019
comment
Вы сделали то же самое, что я сказал, извините, я не видел это внимательно. Я постараюсь найти лучший подход. Спасибо. - person NITESH KUMAR; 15.06.2019