Обозначим:
Ключевое наблюдение: Значение в позиции (индексация с 1) в массиве определяется позицией наименьшего установленного бита (конечных нулей) в . В частности, если позиция имеет конечных нулей в её двоичном представлении, то .
Редакция: Когда все элементы равны некоторой константе , каждое положение в массиве имеет значение . Следовательно:
Временная сложность: за запрос
Пространственная сложность:
Редакция: Эти запросы запрашивают сумму всего массива, построенного после раундов. Мы можем использовать рекурсию:
Это потому, что , так что новая сумма в два раза больше старой суммы плюс .
Временная сложность: за запрос
Пространственная сложность:
Редакция: Для небольших мы можем фактически явно построить массив , так как . Затем мы можем вычислить префиксные суммы и ответить на каждый запрос за .
Временная сложность:
Пространственная сложность:
Редакция: Для точечного запроса в позиции нам нужно выяснить, какой элемент массива находится в позиции . Ключевое понимание заключается в том, что , где — это количество конечных нулей в двоичном представлении (эквивалентно, позиция наименьшего установленного бита, индексируемая с 0 справа).
Алгоритм:
Пример:
Временная сложность: за запрос
Пространственная сложность:
Редакция: Ключевое понимание заключается в том, чтобы эффективно вычислить префиксные суммы без построения массива. Для позиции (индексация с 1) мы можем вычислить , анализируя двоичное представление .
Математическое понимание: Вклад каждого в префиксную сумму можно вычислить, исследуя биты :
где .
Алгоритм работает следующим образом:
Чтобы ответить на запрос : вычислить .
Почему это работает: Каждый элемент появляется в позициях, где количество конечных нулей равно . Количество таких позиций в первых позициях следует шаблону, основанному на двоичном представлении . Член подсчитывает вхождения, а вычитание вклада предыдущего элемента учитывает переход между уровнями.
Временная сложность: за запрос
Пространственная сложность:
function getValue(p):
k = __builtin_ctzll(p) // подсчет конечных нулей
return a[k]
#include <bits/stdc++.h>
using namespace std;
const int64_t MOD = 1e9 + 7;
int64_t a[99], n;
int64_t mul(int64_t x, int64_t y) {
x = (x % MOD + MOD) % MOD;
y = (y % MOD + MOD) % MOD;
return (x * y) % MOD;
}
int64_t add(int64_t x, int64_t y) {
return (MOD + (x + y) % MOD) % MOD;
}
// Вычислить префиксную сумму для позиций от 1 до pos
int64_t prefixSum(int64_t pos) {
int64_t ans = 0, prev = 0;
for (int i = 0; i < n && pos > 0; i++) {
ans = add(ans, mul(-pos, prev));
ans = add(ans, mul(pos, a[i]));
prev = a[i];
pos /= 2;
}
return ans;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int q;
cin >> q;
while (q--) {
int64_t l, r;
cin >> l >> r;
cout << add(prefixSum(r), -prefixSum(l - 1)) << '\n';
}
return 0;
}