Побудуємо дерево відрізків, яке підтримує операцію підсумовування. У кожній вершині (фундаментальному відрізку ) додатково будемо зберігати значення . Якщо left > 0, то у вершині є відкладена операція: всім вершинам її піддерева слід призначити значення left, left + 1, ..., left + b - a. Спочатку у всіх вершинах дорівнює нулю.
Розглянемо процедуру просування значень. Нехай запит q(x, y) частково перетинає обидва піддерева, тобто x \le m < y. У цьому випадку:
Для лівого сина встановлюємо left = 1.
Для правого сина значення встановлюємо на більшим довжини відрізка .
Якщо a \ne x~(b \ne y), то значення слід просувати далі.
Якщо x \ge m + 1 (відрізок запиту повністю лежить у правому піддереві), то значення для лівого сина залишається рівним .
Нехай запит q(x, y) покривається набором фундаментальних відрізків [a_1, b_1], [a_2, b_2], ..., [a_k, b_k]. Тоді значення для відрізка [a_i, b_i] встановлюємо рівним
Наприклад, нехай дерево побудоване на відрізку , а запит q(2, 7) покриває такі фундаментальні відрізки:
Приклад
Розглянемо наступний тест:
5
5 3 2 4 5
2
2 2 5
1 1 3Біля кожної вершини в прямокутнику записана сума на відрізку.
Реалізація алгоритму
Оголосимо масив SegTree для зберігання дерева відрізків.
struct node
{
long long summa;
int left;
};
vector<node> SegTree;Функція BuildTree будує дерево відрізків.
void BuildTree(int v, int lpos, int rpos)
{
if (lpos == rpos)
SegTree[v].sum = a[lpos];
else
{
int mid = (lpos + rpos) / 2;
BuildTree(2 * v, lpos, mid);
BuildTree(2 * v + 1, mid + 1, rpos);
SegTree[v].sum = SegTree[2 * v].sum + SegTree[2 * v + 1].sum;
}
}Функція Sum обчислює суму чисел від до .
long long Sum(int a, int b)
{
return 1LL * (a + b) * (b - a + 1) / 2;
}Функція Push виконує просування значення з вершини в її синів. Виконується перерахунок суми в синах.
void Push(int v, int lpos, int mid, int rpos)
{
if (SegTree[v].left)
{
SegTree[2 * v].left = SegTree[v].left;
SegTree[2 * v].sum = Sum(SegTree[2 * v].left,
SegTree[2 * v].left + mid - lpos);
SegTree[2 * v + 1].left = SegTree[v].left + mid - lpos + 1;
SegTree[2 * v + 1].sum = Sum(SegTree[2 * v + 1].left,
SegTree[2 * v + 1].left + rpos - mid - 1);
SegTree[v].left = 0;
}
}Функція Update заповнює проміжок [left; right] послідовністю значень from, from + 1, from + 2, ... .
void Update(int v, int lpos, int rpos, int left, int right, int from)
{
if (left > right) return;
if ((lpos == left) && (rpos == right))
{
SegTree[v].left = from;
SegTree[v].sum = Sum(from, from + right - left);
return;
}
int mid = (lpos + rpos) / 2;
int midval = mid - left + 1;Якщо відрізок запиту [left; right] повністю лежить в правому піддереві [mid + 1; rpos], то передаємо правому сину значення без змін (при цьому встановлюємо рівним нулю).
if (midval < 0) midval = 0;
Push(v, lpos, mid, rpos);
Update(2 * v, lpos, mid, left, min(mid, right), from);
Update(2 * v + 1, mid + 1, rpos, max(left, mid + 1),
right, from + midval);
SegTree[v].sum = SegTree[2 * v].sum + SegTree[2 * v + 1].sum;
}Функція Summa повертає суму чисел на відрізку [left; right].
long long Summa(int v, int lpos, int rpos, int Left, int Right)
{
if (Left > Right) return 0;
if ((lpos == Left) && (rpos == Right)) return SegTree[v].sum;
int mid = (lpos + rpos) / 2;
Push(v, lpos, mid, rpos);
return Summa(2 * v, lpos, mid, Left, min(mid, Right)) +
Summa(2 * v + 1, mid + 1, rpos, max(Left, mid + 1), Right);
}Основна частина програми. Читаємо вхідний масив чисел.
scanf("%d", &n);
a.resize(n + 1);
for(i = 1; i <= n; ++i)
scanf("%d", &a[i]);Будуємо дерево відрізків.
SegTree.resize(4 * n);
BuildTree(1, 1, n);Послідовно обробляємо запитів.
scanf("%d", &q);
for(i = 0; i < q; ++i)
{
scanf("%d %d %d", &type, &l, &r);
if(type == 1)
printf("%lld\n", Summa(1,1,n,l,r));
else
Update(1,1,n,l,r,1);
}