Имеется массив целых чисел длины n. Вам следует ответить на q
запросов: сколько чисел из интервала [l, r] имеют значение меньше x.
Вход. Первая строка содержит длину массива n (1 ≤ n ≤ 2 * 105).
Следующая строка содержит n чисел. В
следующей строке задано количество q (1 ≤ q
≤ 105) запросов. Каждая из следующих q строк содержит один запрос: три целых числа l, r и x (l ≤ r, 1 ≤ x ≤ 109).
Выход. Для каждого
запроса выведите в отдельной строке количество чисел из интервала [l, r], которые меньше x.
Пример
входа |
Пример
выхода |
8 1 3 2 4 3 10
5 5 4 1 8 5 1 4 3 5 8 9 2 6 4 |
5 2 3 3 |
структуры
данных – дерево отрезков
Алгоритм Merge tree. Заведем в
каждой вершине дерева отрезков дополнительный массив. Вершина, соответствующая
фундаментальному отрезку [i; j], содержит в своем массиве
отсортированный набор чисел ai,
…, aj. Время построения такого
дерева составляет O(nlog2n).
Тогда для
нахождения количества чисел из интервала [l, r], которые меньше x, следует разбить этот интервал на множество фундаментальных
отрезков и в каждом из них совершить бинарный поиск, подсчитав количество
элементов, меньших x. Время запроса
составит .
Пример
Объявим дерево отрезков.
В каждой ее вершине храним массив чисел.
vector<vector<int> > SegTree;
Входные числа храним в массиве а.
vector<int> a;
Функция BuildTree строит дерево отрезков по массиву а.
Отсортированный массив в каждой вершине получается в результате слияния
массивов ее сыновей за O(n) при помощи
команды merge.
void BuildTree(int v, int lpos, int rpos)
{
if (lpos == rpos)
SegTree[v].push_back(a[lpos]);
else
{
int mid = (lpos + rpos) / 2;
BuildTree(2*v, lpos, mid);
BuildTree(2*v+1, mid+1, rpos);
SegTree[v].resize(rpos - lpos + 1);
merge(SegTree[2*v].begin(), SegTree[2*v].end(),
SegTree[2*v+1].begin(),
SegTree[2*v+1].end(),
SegTree[v].begin());
}
}
Функция Query подсчитывает
количество чисел на интервале [l, r], которые меньше x. Для каждого фундаментального отрезка [left; right] при помощи
функции lower_bound находим количество чисел, меньших x.
int Query(int v, int lpos, int rpos, int left, int right, int x)
{
if (left > right) return 0;
if ((lpos == left) && (rpos == right))
return
lower_bound(SegTree[v].begin(),
SegTree[v].end(), x) –
SegTree[v].begin();
int mid = (lpos + rpos) / 2;
return Query(2*v, lpos, mid, left, min(mid, right), x) +
Query(2*v+1, mid+1, rpos, max(left, mid+1), right, x);
}
Основная часть программы. Читаем входной массив чисел.
scanf("%d",&n);
for(i = 1; i <= n; i++)
scanf("%d",&a[i]);
Строим по массиву а дерево отрезков.
SegTree.resize(4*n);
build(1,1,n);
Последовательно выполняем q
запросов.
scanf("%d",&q);
for(i = 0; i < q; i++)
{
scanf("%d %d
%d",&l,&r,&x);
printf("%d\n",Query(1,1,n,l,r,x));
}
#include <cstdio>
#include <vector>
#include <set>
#include <algorithm>
#include <cmath>
using namespace std;
vector<int> a;
vector<vector<int> > b;
int i, n, q, len, blocks,
l, r, x;
int main()
{
scanf("%d", &n);
a.resize(n);
len = sqrt(n)
+ 1;
blocks =
ceil((double)n / len);
b.resize(len);
for (i = 0;i < n; i++)
{
scanf("%d", &a[i]);
b[i / len].push_back(a[i]);
}
scanf("%d", &q);
for (i = 0;i < blocks; i++)
sort(b[i].begin(), b[i].end());
while (q--)
{
scanf("%d %d %d", &l,
&r, &x);
l--;
r--;
int res = 0;
int c_l = l / len, c_r = r / len;
if (c_l == c_r)
{
for (i = l; i <= r; i++)
if (a[i] < x) res++;
}
else
{
for (i = l; i <= (c_l + 1)*len - 1; i++)
if (a[i] < x) res++;
for (i = c_l + 1; i <= c_r - 1; i++)
res
+= lower_bound(b[i].begin(), b[i].end(), x) - b[i].begin();
for (i = c_r * len; i <= r; i++)
if (a[i] < x) res++;
}
printf("%d\n", res);
}
return 0;
}