НОД, Сумма, УмножитьИмя входного файла: стандартны...

تم الإنشاء في: ١٢ نوفمبر ٢٠٢٦

تمت الإجابة باستخدام GPT-6 بواسطة Chat01

سؤال

НОД, Сумма, УмножитьИмя входного файла:
стандартный вводИмя выходного файла:
стандартный выводОграничение по времени:
3 секундыОграничение по памяти:
256 мегабайтВам дан массив a, состоящий из n целых чисел. Вам также даны q запросов [l;r]. Для каждогозапроса найдите максимальное значение sum[tl;tr]  gcd[tl;tr] среди всех пар (tl;tr), где
• l 6 tl 6 tr 6 r;• sum[tl;tr]  сумма всех чисел в отрезке [tl;tr];• gcd[tl;tr]  наибольший общий делитель всех чисел в отрезке [tl;tr].
Наибольший общий делитель двух чисел a и b  это наибольшее положительное целое число x,которое делит и a, и b.Наибольший общий делитель множества чисел  это наибольшее положительное целое число x,которое делит все элементы множества.Формат входных данныхПервая строка содержит два целых числа n, q (1 6 n;q 6 2  100000)  количество элементов вмассиве и количество запросов соответственно.Вторая строка содержит n целых чисел a
i
(1 6 a
i
6 6  10
6)  описание массива.Каждая из следующих q строк содержит два целых числа l
j
, r
j
(1 6 l
j
6 r
j
6 n)  описаниезапросов.Формат выходных данныхВыведите q целых чисел  ответы на запросы.Система оценкиБаллы за каждую подзадачу начисляются только в случае, если все тесты для этой подзадачии необходимых подзадач успешно пройдены.
Подзадача Баллы
Дополнительныеограничения
Необходимыеподзадачи
Информацияо проверке0
0
Тесты из условия
полная1
4
n 6 3
первая ошибка2
8
n;q 6 1000
первая ошибка3
5
n 6 1000
2
первая ошибка4
17
n;q 6 100000
2
первая ошибка5
14
n 6 100000
2, 3, 4
первая ошибка6
5
Все a
i
6 20
первая ошибка7
7
Все a
i
6 1000
6
первая ошибка8
16
Все l
j
= 1
первая ошибка9
24
нет
1 – 8
первая ошибка
Страница 1 из 2Примерыстандартный ввод
стандартный вывод3 23 3 21 32 3
189
8 62 4 8 8 8 2 4 161 82 53 42 47 73 6
2
Как решать когда максимальный элемент до 20

الإجابة

Если все ai≤20a_i \le 20, можно использовать то, что НОД любого подотрезка тоже не превосходит 20.
Получим решение за O(20(n+q)log⁡n)O(20(n+q)\log n) с помощью дерева отрезков.

Основная идея

Нужно найти
max⁡l≤L≤R≤rsum⁡(L,R)⋅gcd⁡(L,R).\max_{l\le L\le R\le r} \operatorname{sum}(L,R)\cdot\gcd(L,R).
Зафиксируем возможный НОД dd, где 1≤d≤201\le d\le20.
Если gcd⁡(L,R)=d\gcd(L,R)=d, то все элементы на отрезке [L,R][L,R] делятся на dd.
Поэтому можно для каждого dd искать максимальную сумму подотрезка, состоящего только из чисел, кратных dd.
Заметь, что мы не требуем, чтобы НОД был ровно dd. Это не мешает!
Действительно, если у найденного подотрезка НОД равен gg, причём d∣gd\mid g, то
sum⁡(L,R)⋅d≤sum⁡(L,R)⋅g.\operatorname{sum}(L,R)\cdot d \le \operatorname{sum}(L,R)\cdot g.
А когда мы переберём d=gd=g, этот подотрезок тоже будет рассмотрен. Значит, максимум получится правильным.

Как отвечать на запросы

Для каждого d∈[1,20]d\in[1,20] построим дерево отрезков.
В дереве для dd:
  • Если aia_i делится на dd, положим bi=aib_i=a_i.
  • Иначе считаем эту позицию запрещённой: подотрезок не может проходить через неё.
В каждой вершине дерева храним:
  • sum — сумму всего отрезка, если все его элементы разрешены;
  • pref — максимальную сумму разрешённого префикса;
  • suff — максимальную сумму разрешённого суффикса;
  • best — максимальную сумму разрешённого подотрезка.
Объединяем две вершины стандартно, как в задаче о максимальной сумме подотрезка:
best=max⁡(bestL,bestR,suffL+prefR).best=\max(best_L,best_R,suff_L+pref_R).
Запрещённые позиции удобно представлять значением −∞-\infty для всех четырёх полей листа. При объединении используем обычные формулы для максимальной суммы подотрезка.
Для запроса [l,r][l,r] перебираем все dd от 1 до 20, получаем best на этом отрезке и обновляем ответ:
ans=max⁡(ans,d⋅bestd(l,r)).ans=\max(ans,d\cdot best_d(l,r)).

Сложность

Построение 20 деревьев:
O(20n).O(20n).
Каждый запрос — 20 обращений к деревьям:
O(20log⁡n).O(20\log n).
Итого:
O(20n+20qlog⁡n)\boxed{O(20n+20q\log n)}
Память:
O(20n).\boxed{O(20n)}.
При n,q≤2⋅105n,q\le 2\cdot10^5 это вполне подходит.
Дополнительное упрощение: поскольку все aia_i положительные, для фиксированного dd максимальная сумма достигается на целом непрерывном блоке чисел, делящихся на dd. Поэтому вместо дерева максимальной суммы подотрезка можно хранить информацию о таких блоках, но описанное решение с sum/pref/suff/best проще реализовать и доказать.
Text version

Если все ai≤20a_i \le 20, можно использовать то, что НОД любого подотрезка тоже не превосходит 20.

Получим решение за O(20(n+q)log⁡n)O(20(n+q)\log n) с помощью дерева отрезков.

Основная идея

Нужно найти

max⁡l≤L≤R≤rsum⁡(L,R)⋅gcd⁡(L,R).\max_{l\le L\le R\le r} \operatorname{sum}(L,R)\cdot\gcd(L,R).

Зафиксируем возможный НОД dd, где 1≤d≤201\le d\le20.

Если gcd⁡(L,R)=d\gcd(L,R)=d, то все элементы на отрезке [L,R][L,R] делятся на dd.

Поэтому можно для каждого dd искать максимальную сумму подотрезка, состоящего только из чисел, кратных dd.

Заметь, что мы не требуем, чтобы НОД был ровно dd. Это не мешает!

Действительно, если у найденного подотрезка НОД равен gg, причём d∣gd\mid g, то

sum⁡(L,R)⋅d≤sum⁡(L,R)⋅g.\operatorname{sum}(L,R)\cdot d \le \operatorname{sum}(L,R)\cdot g.

А когда мы переберём d=gd=g, этот подотрезок тоже будет рассмотрен. Значит, максимум получится правильным.

Как отвечать на запросы

Для каждого d∈[1,20]d\in[1,20] построим дерево отрезков.

В дереве для dd:

  • Если aia_i делится на dd, положим bi=aib_i=a_i.
  • Иначе считаем эту позицию запрещённой: подотрезок не может проходить через неё.

В каждой вершине дерева храним:

  • sum — сумму всего отрезка, если все его элементы разрешены;
  • pref — максимальную сумму разрешённого префикса;
  • suff — максимальную сумму разрешённого суффикса;
  • best — максимальную сумму разрешённого подотрезка.

Объединяем две вершины стандартно, как в задаче о максимальной сумме подотрезка:

best=max⁡(bestL,bestR,suffL+prefR).best=\max(best_L,best_R,suff_L+pref_R).

Запрещённые позиции удобно представлять значением −∞-\infty для всех четырёх полей листа. При объединении используем обычные формулы для максимальной суммы подотрезка.

Для запроса [l,r][l,r] перебираем все dd от 1 до 20, получаем best на этом отрезке и обновляем ответ:

ans=max⁡(ans,d⋅bestd(l,r)).ans=\max(ans,d\cdot best_d(l,r)).

Сложность

Построение 20 деревьев:

O(20n).O(20n).

Каждый запрос — 20 обращений к деревьям:

O(20log⁡n).O(20\log n).

Итого:

O(20n+20qlog⁡n)\boxed{O(20n+20q\log n)}

Память:

O(20n).\boxed{O(20n)}.

При n,q≤2⋅105n,q\le 2\cdot10^5 это вполне подходит.

Дополнительное упрощение: поскольку все aia_i положительные, для фиксированного dd максимальная сумма достигается на целом непрерывном блоке чисел, делящихся на dd. Поэтому вместо дерева максимальной суммы подотрезка можно хранить информацию о таких блоках, но описанное решение с sum/pref/suff/best проще реализовать и доказать.

سؤال

А полное решение

الإجابة

Text version

Вот полное решение на C++17 для подзадачи, где ai≤20a_i \le 20.

Идея: перебираем возможный НОД dd от 1 до 20. Для каждого dd строим дерево отрезков, которое находит максимальную сумму подотрезка, состоящего только из чисел, кратных dd.

cpp
#include <bits/stdc++.h> using namespace std; using ll = long long; const ll INF = (1LL << 60); const int K = 20; struct Node { ll sum, pref, suff, best; }; Node emptyNode() { return {0, -INF, -INF, -INF}; } Node mergeNode(Node a, Node b) { Node c; c.sum = a.sum + b.sum; c.pref = max(a.pref, a.sum + b.pref); c.suff = max(b.suff, b.sum + a.suff); c.best = max({a.best, b.best, a.suff + b.pref}); return c; } struct SegTree { int n; vector<Node> t; SegTree(const vector<int>& a, int d) { n = (int)a.size() - 1; t.resize(4 * n); build(1, 1, n, a, d); } void build(int v, int l, int r, const vector<int>& a, int d) { if (l == r) { if (a[l] % d == 0) { ll x = a[l]; t[v] = {x, x, x, x}; } else { t[v] = {-INF, -INF, -INF, -INF}; } return; } int m = (l + r) / 2; build(v * 2, l, m, a, d); build(v * 2 + 1, m + 1, r, a, d); t[v] = mergeNode(t[v * 2], t[v * 2 + 1]); } Node query(int v, int l, int r, int ql, int qr) { if (ql <= l && r <= qr) { return t[v]; } int m = (l + r) / 2; if (qr <= m) return query(v * 2, l, m, ql, qr); if (ql > m) return query(v * 2 + 1, m + 1, r, ql, qr); Node a = query(v * 2, l, m, ql, qr); Node b = query(v * 2 + 1, m + 1, r, ql, qr); return mergeNode(a, b); } ll query(int l, int r) { return query(1, 1, n, l, r).best; } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, q; cin >> n >> q; vector<int> a(n + 1); for (int i = 1; i <= n; i++) { cin >> a[i]; } vector<SegTree> trees; trees.reserve(K); for (int d = 1; d <= K; d++) { trees.emplace_back(a, d); } while (q--) { int l, r; cin >> l >> r; ll ans = 0; for (int d = 1; d <= K; d++) { ll s = trees[d - 1].query(l, r); if (s > 0) { ans = max(ans, s * d); } } cout << ans << '\n'; } return 0; }

Почему это работает?

Рассмотрим произвольный подотрезок [L,R][L,R] с НОД gg.

Поскольку все ai≤20a_i\le20, имеем 1≤g≤201\le g\le20.

Когда алгоритм рассматривает d=gd=g, все элементы этого подотрезка делятся на dd. Значит, дерево для dd найдёт сумму не меньше, чем sum⁡(L,R)\operatorname{sum}(L,R).

Поэтому ответ алгоритма не меньше оптимального.

С другой стороны, пусть дерево для некоторого dd выбрало подотрезок с суммой SS. Все элементы этого подотрезка делятся на dd, поэтому его настоящий НОД gg кратен dd.

Следовательно,

S⋅d≤S⋅g.S\cdot d\le S\cdot g.

То есть значение, найденное алгоритмом, не превосходит ответа исходной задачи.

Из двух неравенств получаем, что алгоритм находит точный максимум.

Сложность

  • Построение всех деревьев: O(20n)O(20n).
  • Один запрос: O(20log⁡n)O(20\log n).
  • Все запросы: O(20qlog⁡n)O(20q\log n).
  • Память: O(20n)O(20n).

Важно: это решение только для подзадачи ai≤20a_i\le20, а не для полных ограничений ai≤6⋅106a_i\le6\cdot10^6.

В реализации используется long long, поскольку ответ может достигать порядка n⋅202n\cdot20^2, а в общей задаче — значительно больше.

مشاركة هذا السؤال والجواب