О! Спасибо. Тогда это и ДО по идее за log n можно сделать
Есть еще такое решение. Посчитаем dp[i] = j - ближайшая справа от i позиция такая что arr[i] == arr[j].
Тогда ответ на запрос [L, R] - количество элементов dp[i] > R на отрезке. Построим ДО на элементах.
Пусть элементы массива лежат из диапазона 0 <= dp[i] <= n. Изначально в корне дерева лежит весь массив dp в исходном порядке и корень отвечает за диапазон [0, n]. Посчитаем середину отрезка: mid = (0 + n) / 2 и в корне создадим дополнительный массив b[i] = bool(dp[i] >= mid), то есть, b[i] = 1, если dp[i] >= mid, и 0 иначе. Теперь посчитаем префикс суммы для массива b: s[i] = s[i-1] + b[i]. Все элементы, dp[i] < mid, отправим в левое поддерево, а dp[i] >= mid - в правое, сохраняя порядок.
Построение за O(n log(n)) памяти и времени.
Как отвечать на запрос за O(log(n)) теперь? Если уметь отвечать за O(1) в каждом узле, то это будет O(log(n)).
Пусть запрос {L, R, X}: сколько элементов dp[L..R] >= X. Тогда если X <= mid, то это, то это s[R] - s[l-1] + запрос к левому поддереву что-то вроде {L-S[L], R-S[R], X}, иначе x > mid и продолжаем с правым поддеревом, пересчитывая L и R за O(1).