Кількість пар дорівнює n \cdot (n + 1) / 2 і при n = 2 \cdot 10^5 становить близько 2 \cdot 10^{10}, тому перебрати всі відрізки неможливо. Обчислюватимемо суму методом "розділяй і володарюй", розбиваючи масив за позицією максимального елемента.
Розбиття за максимумом. Нехай потрібно знайти суму P(i, j) за всіма відрізками , що лежать усередині відрізка . Знайдемо позицію максимального елемента на . Кожен відрізок [i; j] \subseteq [b; e] належить рівно до одного з трьох видів:
містить позицію , тобто b \le i \le m \le j \le e;
цілком лежить у [b; m - 1];
цілком лежить у [m + 1; e].
Відрізки другого і третього видів обробляємо рекурсивно. Для відрізків першого виду максимум дорівнює a_m, тому M(i, j) = a_m, і їхній сумарний внесок дорівнює
Залишилося швидко обчислити суму НСД за всіма відрізками, що містять позицію .
Розкладання НСД. Відрізок , що містить , складається з лівої частини і правої частини . Тому
Отже, НСД відрізка визначається парою значень: НСД лівої частини і НСД правої частини.
Групи кінців. Зі збільшенням значення G(m, j) не зростає. Більше того, якщо воно змінюється, то нове значення є власним дільником попереднього і тому менше за нього щонайменше вдвічі. Отже, G(m, j) набуває не більше ніж \lfloor \log_2 C \rfloor + 1 різних значень, де — найбільше значення елемента. При C \le 10^9 це не більше ніж \lfloor \log_2 C \rfloor + 1 = 30 значень.
Позиції з однаковим значенням G(m, j) ідуть поспіль. Назвемо такий блок позицій групою. Кожну групу описуватимемо парою (g, \mathit{cnt}), де — значення НСД, а — кількість позицій у групі. Аналогічно на групи розбиваються ліві кінці за значенням .
Кінець групи знаходимо двійковим пошуком. Якщо група починається в позиції і g = G(m, j), то рівність G(m, t) = g виконується для всіх від до кінця групи і порушується для всіх після нього.
Сума за парами груп. Нехай ліва група має вигляд (g_L, \mathit{cnt}_L), а права — (g_R, \mathit{cnt}_R). Тоді всі \mathit{cnt}_L \cdot \mathit{cnt}_R відрізків, лівий кінець яких лежить у лівій групі, а правий — у правій, мають один і той самий НСД \gcd(g_L, g_R). Тому
де друга сума береться за всіма парами з лівої і правої груп. Кількість таких пар не перевищує 30 \cdot 30.
Запити на відрізку. Для пошуку позиції максимуму та обчислення НСД на відрізку використовуємо дві розріджені таблиці (Sparse Table). Обидві операції ідемпотентні: \max(x, x) = x і \gcd(x, x) = x. Тому запит на відрізку обробляється за двома блоками довжини 2^k, що перекриваються.
Значення дуже велике, тому всі обчислення виконуємо за модулем 10^9 + 7.
Складність. Кожен рекурсивний виклик виключає з розгляду одну позицію , тому викликів не більше ніж . У кожному виклику з кожного боку не більше ніж O(\log C) груп. Кінець групи знаходиться двійковим пошуком за O(\log n) запитів НСД, а кожен запит виконується за O(\log C). Перебір пар груп потребує O(\log^2 C) обчислень НСД. Побудова розріджених таблиць виконується за O(n \log n \cdot \log C). Підсумкова складність становить O(n \log^2 C \cdot (\log n + \log C)). На практиці груп значно менше ніж , і алгоритм працює швидко.
Приклад
Розглянемо другий приклад: A = (2, 4, 6, 12, 3). Позиції нумеруємо з одиниці.
Перший виклик обробляє весь масив . Максимальний елемент a_4 = 12, тому m = 4. На рисунку показано групи лівих і правих кінців.
Ліві кінці: G(4, 4) = 12, G(3, 4) = \gcd(6, 12) = 6, G(2, 4) = G(1, 4) = 2. Отримуємо групи , і .
Праві кінці: G(4, 4) = 12, G(4, 5) = \gcd(12, 3) = 3. Отримуємо групи і .
Переберемо всі пари груп:
ліва група | права група | НСД | відрізків | внесок |
(12, 1) | (12, 1) | 12 | 1 | 12 |
(12, 1) | (3, 1) | 3 | 1 | 3 |
(6, 1) | (12, 1) | 6 | 1 | 6 |
(6, 1) | (3, 1) | 3 | 1 | 3 |
(2, 2) | (12, 1) | 2 | 2 | 4 |
(2, 2) | (3, 1) | 1 | 2 | 2 |
Сума НСД за відрізками, що містять позицію , дорівнює 12 + 3 + 6 + 3 + 4 + 2 = 30, а внесок цих відрізків дорівнює 12 \cdot 30 = 360. Наприклад, останній рядок таблиці відповідає відрізкам і : для кожного з них НСД дорівнює 1, а максимум дорівнює 12.
Далі рекурсивно обробляються відрізки і . Усі виклики наведено в таблиці:
відрізок | \boldsymbol{a_m} | ліві групи | праві групи | сума НСД | внесок | |
\boldsymbol{[1; 5]} | 4 | 12 | (12, 1), (6, 1), (2, 2) | (12, 1), (3, 1) | 30 | 360 |
\boldsymbol{[1; 3]} | 3 | 6 | (6, 1), (2, 2) | (6, 1) | 10 | 60 |
\boldsymbol{[1; 2]} | 2 | 4 | (4, 1), (2, 1) | (4, 1) | 6 | 24 |
\boldsymbol{[1; 1]} | 1 | 2 | (2, 1) | (2, 1) | 2 | 4 |
\boldsymbol{[5; 5]} | 5 | 3 | (3, 1) | (3, 1) | 3 | 9 |
Відповідь дорівнює 360 + 60 + 24 + 4 + 9 = 457.
Реалізація алгоритму
Оголосимо константи:
\mathit{MOD} = 10^9 + 7 — модуль,
\mathit{LOG} = 18 — найбільший показник степеня в розріджених таблицях (2^{18} > 2 \cdot 10^5).
Оголосимо тип ll.
#define MOD 1000000007LL
#define LOG 18 // 2^18 = 262144 > 2*10^5
typedef long long ll;Послідовність зберігаємо в масиві ; елементи нумеруються з нуля. Значення дорівнює \lfloor \log_2 \mathit{len} \rfloor. Розріджена таблиця \mathit{spMax}[i][k] зберігає індекс максимального елемента, а \mathit{spGcd}[i][k] — НСД елементів на відрізку [i; i + 2^k - 1].
vector<int> a;
vector<int> lg2; // lg2[len] = floor(log2(len))
// spMax[i][k] — індекс максимуму на [i, i + 2^k)
vector<vector<int>> spMax;
// spGcd[i][k] — НСД на [i, i + 2^k)
vector<vector<int>> spGcd;Функція gcd обчислює НСД двох чисел алгоритмом Евкліда.
int gcd(int x, int y)
{
while (y) { int t = x % y; x = y; y = t; }
return x;
}Функція better із двох індексів та повертає той, у якому елемент масиву більший. При рівності елементів повертається .
int better(int i, int j)
{
return a[i] >= a[j] ? i : j;
}Функція build заповнює масив і будує обидві розріджені таблиці за O(n \log n \cdot \log C), C \le 10^9.
void build()
{
lg2.assign(n + 1, 0);
for (int len = 2; len <= n; len++) lg2[len] = lg2[len / 2] + 1;
spMax.assign(n, vector<int>(LOG + 1));
spGcd.assign(n, vector<int>(LOG + 1));
for (int i = 0; i < n; i++)
{
spMax[i][0] = i;
spGcd[i][0] = a[i];
}
for (int k = 1; (1 << k) <= n; k++)
for (int i = 0; i + (1 << k) <= n; i++)
{
int h = 1 << (k - 1); // половина довжини блоку
spMax[i][k] = better(spMax[i][k - 1], spMax[i + h][k - 1]);
spGcd[i][k] = gcd(spGcd[i][k - 1], spGcd[i + h][k - 1]);
}
}Функція qMax повертає індекс максимального елемента на відрізку [l; r].
int qMax(int l, int r)
{
int k = lg2[r - l + 1];
return better(spMax[l][k], spMax[r - (1 << k) + 1][k]);
}Функція qGcd повертає НСД елементів на відрізку [l; r]. Блоки довжини 2^k можуть перекриватися, але на результат це не впливає, оскільки \gcd(x, x) = x.
int qGcd(int l, int r)
{
int k = lg2[r - l + 1];
return gcd(spGcd[l][k], spGcd[r - (1 << k) + 1][k]);
}Функція lastRight знаходить найправішу позицію t \in [\mathit{from}; e], для якої G(m, t) = g. Тут g = G(m, \mathit{from}). Оскільки зі зростанням не зростає, рівність виконується на деякій початковій ділянці відрізка , і двійковий пошук знаходить її останню позицію. Середина обчислюється як (\mathit{lo} + \mathit{hi} + 1) / 2, щоб під час присвоєння \mathit{lo} = \mathit{mid} пошук не зациклювався.
int lastRight(int m, int from, int e, int g)
{
int lo = from, hi = e;
while (lo < hi)
{
int mid = (lo + hi + 1) / 2;
if (qGcd(m, mid) == g) lo = mid; else hi = mid - 1;
}
return lo;
}Функція firstLeft симетрично знаходить найлівішу позицію t \in [b; \mathit{from}], для якої G(t, m) = g.
int firstLeft(int m, int from, int b, int g)
{
int lo = b, hi = from;
while (lo < hi)
{
int mid = (lo + hi) / 2;
if (qGcd(mid, m) == g) hi = mid; else lo = mid + 1;
}
return lo;
}Структура Group описує групу кінців відрізків:
— значення НСД,
— кількість позицій у групі.
struct Group { int g; ll cnt; };Функція solve повертає за модулем суму P(i, j) за всіма відрізками , що лежать усередині . Для порожнього відрізка сума дорівнює .
ll solve(int b, int e)
{
if (b > e) return 0;Знаходимо позицію максимального елемента на .
int m = qMax(b, e);Розбиваємо праві кінці j = m, m + 1, \ldots, e на групи. Для поточного обчислюємо g = G(m, j) і функцією lastRight знаходимо останню позицію групи. Група містить t - j + 1 позицій; наступна група починається з позиції t + 1.
vector<Group> R;
for (int j = m; j <= e; )
{
int g = qGcd(m, j);
int t = lastRight(m, j, e, g);
R.push_back({ g, t - j + 1 });
j = t + 1;
}Аналогічно розбиваємо на групи ліві кінці i = m, m - 1, \ldots, b. Група займає позиції від до і містить i - t + 1 позицій; наступна група закінчується в позиції t - 1.
vector<Group> L;
for (int i = m; i >= b; )
{
int g = qGcd(i, m);
int t = firstLeft(m, i, b, g);
L.push_back({ g, i - t + 1 });
i = t - 1;
}Обчислюємо суму значень НСД за всіма відрізками, що містять позицію . Для кожної пари груп додаємо \gcd(l.g, r.g) \cdot l.\mathit{cnt} \cdot r.\mathit{cnt}. Добуток l.\mathit{cnt} \cdot r.\mathit{cnt} не перевищує 4 \cdot 10^{10} і вміщується в тип long long. Беремо його за модулем ; після цього добуток НСД (менше за 2^{30}) на число, менше за , також вміщується в long long.
ll sumG = 0;
for (Group &l : L)
for (Group &r : R)
{
ll cnt = (l.cnt * r.cnt) % MOD;
sumG = (sumG + gcd(l.g, r.g) % MOD * cnt) % MOD;
}Максимум на всіх відрізках, що містять , дорівнює a_m, тому їхній сумарний внесок дорівнює a_m \cdot \mathit{sumG}.
ll res = (ll)a[m] % MOD * sumG % MOD;Додаємо рекурсивно обчислені суми для відрізків, що лежать цілком ліворуч і праворуч від позиції .
return (res + solve(b, m - 1) + solve(m + 1, e)) % MOD;
}Основна частина програми. Читаємо вхідні дані.
scanf("%d", &n);
a.resize(n);
for (int &x : a) scanf("%d", &x);Будуємо розріджені таблиці.
build();Виводимо відповідь для всього масиву — відрізка [0; n - 1].
printf("%lld\n", solve(0, n - 1));