Как посчитать сумму min(subarray) * sum(subarray) для всех подмассивов за O(n)

Решаю задачу, пока ничего не получилось, решения либо неправильные, либо слишком медленные (время должно быть O(n)), прошу помочь с написанием быстрого алгоритма.

  • Появилась идея проходить циклом по первым элементам подмассивов, а вложенным по концам, используя результаты прошлых циклов, решение таким образом составляет O(n2).

  • Также мне посоветовали для каждого отдельного элемента массива находить первые справа и слева элементы, что меньше него и находить сумму всех подмассивов, что имеют в себе этот элемент и умножать на него и добавлять в ответ.

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

источник полного условия

Будучи правителем королевства, вы имеете в своем распоряжении целую армию волшебников.

Вам дан массив целых чисел c силой волшебника strength. Для непрерывной группы волшебников (т.е. силы волшебников образуют подмассив) общая сила определяется как произведение следующих двух значений:

  • Сила самого слабого волшебника в группе.
  • Сумма индивидуальных сил всех волшебников в группе.

Верните сумму сил всех смежных групп волшебников. Поскольку ответ может быть очень большим, верните его по модулю 10^9 + 7.

Подмассив — это непрерывная непустая последовательность элементов внутри массива.


Примеры

input.txt output.txt
1 3 1 2 44
5 4 6 213

Пояснение к примеру 1

Ниже приведены все смежные группы волшебников:

  • [1] из [**1**, 3, 1, 2] имеет общую силу min([1]) * sum([1]) = 1 * 1 = 1
  • [3] из [1, **3**, 1, 2] имеет общую силу min([3]) * sum([3]) = 3 * 3 = 9
  • [1] из [1, 3, **1**, 2] имеет общую силу min([1]) * sum([1]) = 1 * 1 = 1
  • [2] из [1, 3, 1, **2**] имеет общую силу min([2]) * sum([2]) = 2 * 2 = 4
  • [1,3] из [**1, 3**, 1, 2] имеет общую силу min([1,3]) * сумма([1,3]) = 1 * 4 = 4
  • [3,1] из [1, **3, 1**, 2] имеет общую силу min([3,1]) * сумма([3,1]) = 1 * 4 = 4
  • [1,2] из [1, 3, **1, 2**] имеет общую силу min([1,2]) * сумма([1,2]) = 1 * 3 = 3
  • [1,3,1] из [**1, 3, 1**, 2] имеет общую силу min([1,3,1]) * сумма([1,3,1]) = 1 * 5 = 5
  • [3,1,2] из [1, **3, 1, 2**] имеет общую силу min([3,1,2]) * сумма([3,1,2]) = 1 * 6 = 6
  • [1,3,1,2] из [**1, 3, 1, 2**] имеет общую силу min([1,3,1,2]) * сумма([1,3,1,2]) = 1 * 7 = 7

Сумма всех общих сил составляет:

1 + 9 + 1 + 4 + 4 + 4 + 3 + 5 + 6 + 7 = 44


Ответы (2 шт):

Автор решения: pavel

Код не пишу. Но идеи должны помочь.

1 - Если взять минимум на всём массиве, то его "вклад" будет равен сумме значений всех отрезков которые его включают.

2 - Как посчитать эту сумму за O(1). Вычислим стандартные префикс суммы Pr[x]. И вычислим F[x] = sum(i, 0, x) {a[i] * (x-i + 1)}. Потом вычислить тоже самое но в обратную сторону. Дальше всё показываю на примере вперёд. Сумма значений всех отрезков вперёд до точки r, содержащие x это S(r,x) = F[r] - F[x-1] - (r - x + 1) * Pr[x] (Примерно так, уточните в отладчике). F[-1] = 0.

3 - Если обсчитать минимум и "удалить" его из массива, то массив распадается на 2 части. Повторять до конца. Конечно удалять нельзя и нужно быстро находить ближайший слева и справа удалённый элемент. Для этой задачи может помочь любое дерево.

Общая сложность - что-то около O(N log N)

→ Ссылка
Автор решения: Stanislav Volodarskiy

Разделяй и властвуй. Пусть a - массив элементов, j индекс элемента приблизительно в середине a. Тогда разобьём все подмассивы a на три группы: подмассивы слева он j, подмассивы, содержащие индекс j и подмассивы справа от j. Если мы научимся вычислять сумму для всех подмассивов, содержащих j, за линейное время, то за NlogN сумеем вычислить общую сумму для всего массива.

С этого момента считаем сумму только для отрезков, содержащих j. Предположим, что aj – глобальный минимум в массиве. Если это не так, то вырежем из a такой подмассив, в котором aj – минимум. В дальнейшем переобозначим этот подмассив как a.

Итак есть a и есть aj – его минимум.

s[i,k] = ∑m∈[i,k] am – cумма элементов массива на отрезке [i, k].

Si,k – сумма всех подмассивов массива a таких, что i ≤ j ≤ k.

Si,k = ∑m∈[i,j],n∈[j,k] s[m,n]

Si,k - Si,k-1 = ∑m∈[i,j] s[m,k] = ∑m∈[i,j] (s[m,j-1] + aj + s[j+1,k]) =
= ∑m∈[i,j] s[m,j-1] + ∑m∈[i,j] (aj + s[j+1,k]) = ∑m∈[i,j] s[m,j-1] + (j - i + 1)(aj + s[j+1,k]).

Обозначим левое слагаемое Qi = ∑m∈[i,j] s[m,j-1]. Аналогично Qk = ∑n∈[j,k] s[j+1,n]. Обозначения Qi и Qk не конфликтуют, так как i ≤ j и j ≤ k.

Тогда Si,k вычисляется из Si,k-1 за константу:

Si,k = Si,k-1 + Qi + (j - i + 1)(aj + s[j+1,k]).

В целом переход от k - 1 к k выглядит как:

s[j+1,k] = s[j+1,k-1] + ak
Qk = Qk-1 + s[j+1,k]
Si,k = Si,k-1 + Qi + (j - i + 1)(aj + s[j+1,k]).

Аналогичный переход от i + 1 к i:

s[i,j-1] = ai + s[i+1,j-1]
Qi = Qi+1 + s[i,j-1]
Si,k = Si+1,k + (k - j + 1)(s[i,j-1] + aj) + Qk.

Оба перехода делаются за константу. В целом за линейное время мы соберём сумму всех подмассивов по всему массиву.

Напомню что aj до сих пор предполагается глобальным минимумом. И найденную сумму надо будет домножить на aj.

Что делать когда отрезок массива a, на котором aj – глобальный минимум, исчерпан? Разберу случай когда этот отрезок с обоих концов ограничен элементами, которые меньше aj. Из этих элементов надо выбрать максимум, обновить наш "минимум" и продолжить "экспансию" на большем отрезке. Процесс повторять пока не будет обработан весь массив.

Обработка займёт линейное время. Если вы сюда пробрались через лес сомнительных обозначений, вас ждёт награда – решение за NlogN:

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

size_t a_size = 0;
size_t a_capacity = 0;
int32_t *a_data = NULL;

void read_a() {
    int32_t ai;
    while (scanf("%" SCNd32, &ai) == 1) {
        if (a_capacity == a_size) {
            size_t capacity = 2 * a_size + 1;
            int32_t *data = realloc(a_data, sizeof(*a_data) * capacity);
            if (data == NULL) {
                exit(1);
            }
            a_capacity = capacity;
            a_data = data;
        }
        a_data[a_size++] = ai;
    }
}

void release_a() {
    a_size = 0;
    a_capacity = 0;
    free(a_data);
}

int32_t mod(int64_t x) {
    const int32_t mod = 1000000007;
    int32_t y = (int32_t)(x % mod);
    return (y >= 0) ? y : mod + y;
}

int32_t add(int32_t a, int32_t b) {
    return mod(a + b);
}

int32_t mul(int32_t a, int32_t b) {
    return mod((int64_t)a * b);
}

int32_t dc_a(const int32_t ii, const int32_t kk) {
    if (ii + 1 == kk) {
        return 0;
    }

    const int32_t j = ii + (kk - ii) / 2;

    int32_t level = a_data[j];
    int32_t s = mul(a_data[j], a_data[j]);

    int32_t si1 = 0;
    int32_t si2 = 0;
    int32_t i = j - 1;

    int32_t sk1 = 0;
    int32_t sk2 = 0;
    int32_t k = j + 1;

    for (; ; ) {
        /* expand left */
        while (ii < i && a_data[i] >= level) {
            si1 = add(si1, a_data[i]);
            si2 = add(si2, si1);
            s = add(s, mul(level, add(sk2, mul(k - j, add(a_data[j], si1)))));
            --i;
        }

        /* expand right */
        while (k < kk && a_data[k] >= level) {
            sk1 = add(sk1, a_data[k]);
            sk2 = add(sk2, sk1);
            s = add(s, mul(level, add(si2, mul(j - i, add(a_data[j], sk1)))));
            ++k;
        }

        /* update level */
        if (ii < i) {
            if (k < kk) {
                /* level = max(a_data[i], a_data[k]); */
                level = (a_data[i] < a_data[k]) ? a_data[k] : a_data[i];
            } else {
                level = a_data[i];
            }
        } else {
            if (k < kk) {
                level = a_data[k];
            } else {
                break;
            }
        }
    }

    return add(s, add(dc_a(ii, j), dc_a(j, kk)));
}

int main() {
    read_a();
    printf("%" PRId32 "\n", dc_a(-1, (int32_t)a_size));
    release_a();
}
$ gcc -O3 dc.c

$ time -p seq 10000000 | ./a.out
143284477
real 2.64
user 2.65
sys 0.06
→ Ссылка