Мир#12426

Мир (Розбір)

Автор задачі:

Матвій Грицина

Підготували:

Матвій Грицина, Констянтин Денисов

Розбір:

Матвій Грицина

Ідея

Введемо масив  — найбільший серед усіх мінімальних лівих індексів трійок, де є правим елементом. Тобто \text{lft}[k] = i, де (i,j,k) — деяка валідна трійка і найбільший індекс з усіх валідних трійок, які мають правий індекс . Також для кожного такого зберігаємо масив \text{ans}[k] = (j,k) — конкретну пару індексів, що утворює валідну трійку разом із . Причому зі всіх таких пар зберігаємо ту, у якої значення  — найменше.

Також побудуємо масив , де

\text{pref\_max}[i] = \max(\text{lft}[1], \text{lft}[2], \ldots, \text{lft}[i]).

Тоді для запиту (l,r) відповідь існує тоді і тільки тоді, коли \text{pref\_max}[r] \ge l.

Як обчислити масив

Проходимо по масиву зліва направо. Для кожного підтримуємо масив його простих дільників. Також підтримуємо для кожного простого два останніх індекси, де ділить :

  •  — найближчий попередній індекс;

  •  — передостанній попередній індекс.

\text{lst}[i][p] = -1, якщо такого індекса не існує.

Розглянемо два випадки.

Випадок 1: НСД всіх трьох чисел

В такому випадку має спільний простий дільник як з , так і з . Переберемо , і якщо \text{lst}[1][p] \neq -1, тобто зліва від індекса існує хоча б два числа, які діляться на , оновлюємо

\text{lft}[k] = \max(\text{lft}[k], \text{lst}[1][p]).

Випадок 2: НСД трьох чисел

Припустимо, що має спільний простий дільник з та спільний простий дільник з . В такому випадку ми можемо взяти для кожного простого і перевірити, чи НСД чисел на цих позиціях більший за . Тобто якщо

\text{lst}[0][p] \neq \text{lst}[0][q]

і

\gcd\bigl(\text{a}[\min(\text{lst}[0][p], \text{lst}[0][q])],\, \text{a}[\max(\text{lst}[0][p], \text{lst}[0][q])]\bigr) > 1,

тоді ми оновлюємо

\text{lft}[k] = \max(\text{lft}[k], \min(\text{lst}[0][p], \text{lst}[0][q])).

Як відповідати на запити

Для того щоб відповідь на запит (l,r) існувала, для якогось l \le k \le r має виконуватись умова \text{lft}[k] \ge l. Іншими словами, відповідь існує тоді і тільки тоді, коли

\max_{l \le k \le r} \text{lft}[k] \ge l.

Оскільки для k < l маємо \text{lft}[k] < l, замість максимуму на відрізку можна брати максимум на префіксі. Тоді нехай idx = \text{pref\_max}[r]. Якщо idx < l, то відповіді не існує, інакше в якості трійки можна взяти

(idx,\; \text{ans}[idx].\text{first},\; \text{ans}[idx].\text{second}).

Асимптотика

За допомогою решета Ератосфена можна передрахувати всі прості дільники для чисел \le A = 2 \cdot 10^5 за O(A \log A). Обчислюємо масив за O(n d^2 \log A), де  — максимальна кількість простих дільників у числа. При заданих обмеженнях d \le 6, тому це легко вкладається в обмеження. Відповідаємо на кожен запит за .

Отже маємо O(A \log A + n d^2 \log A + q).

#include <bits/stdc++.h>
using namespace std;
const int MAXA = 200010;
const int MAXN = 400000;
vector<int> primes[MAXA];

void sieve() {
  for (int i = 2; i <= MAXA; i++)
    if (primes[i].empty()) {
      for (int j = i; j < MAXA; j += i)
        primes[j].push_back(i);
    }
}

int main() {
  sieve();
  int n;
  cin >> n;
  vector<int> a(n);
  for (auto& ai : a)
    cin >> ai;

  vector<pair<int, int>> ans(n, {-1, -1});
  vector<int> pref_max(n + 1, -1), lft(n, -1);
  vector<vector<int>> lst(2, vector<int>(MAXA, -1));

  for (int i = 0; i < n; i++) {
    int x = a[i];
    for (auto p : primes[x])
      if (lst[1][p] != -1) {
        lft[i] = max(lft[i], lst[1][p]);
        if (ans[lst[1][p]].first == -1)
          ans[lst[1][p]] = {lst[0][p], i};
      }

    for (auto p1 : primes[x])
      for (auto p2 : primes[x]) {
        if (p1 == p2 || lst[0][p1] == lst[0][p2])
          continue;
        if (lst[0][p1] >= 0 && lst[0][p2] >= 0 &&
            __gcd(a[lst[0][p1]], a[lst[0][p2]]) > 1 &&
            min(lst[0][p1], lst[0][p2]) > lft[i]) {
          lft[i] = min(lst[0][p1], lst[0][p2]);
          if (ans[min(lst[0][p1], lst[0][p2])].first == -1)
            ans[min(lst[0][p1], lst[0][p2])] =
              {max(lst[0][p1], lst[0][p2]), i};
        }
      }

    for (auto p : primes[x]) {
      lst[1][p] = lst[0][p];
      lst[0][p] = i;
    }
  }

  for (int i = 1; i < n; i++)
    pref_max[i] = max(pref_max[i - 1], lft[i]);

  int q;
  cin >> q;
  while (q--) {
    int l, r;
    cin >> l >> r;
    --l, --r;
    int idx = pref_max[r];
    if (idx < l) {
      cout << -1 << '\n';
    } else {
      cout << ans[idx].first + 1 << ' '
           << ans[idx].second + 1 << ' '
           << idx + 1 << '\n';
    }
  }
}