Məsələni açıqlayan bir xətti ağaç quracıq, bu ağac toplama əməliyyatını dəstəkləyir. Hər bir zirvədə (əsas segment ) əlavə olaraq dəyərini saxlayacağıq. Əgər left > 0 isə, bu zaman zirvədə gecikdirilmiş bir əməliyyat var: onun alt ağacının bütün zirvələrinə left, left + 1, ..., left + b - a dəyərləri təsis edilməlidir. Əvvəlcə, bütün zirvələrdə 0-a bərabərdir.
Dəyərləri ötürmə prosesinə nəzər salaq. Tutaq ki, sorğu q(x, y) hər iki alt ağacı qismən kəsir, yəni x \le m < y. Bu halda:
Sol övlad üçün left = 1 təsis edirik.
Sağ övlad üçün dəyərini intervalının uzunluğu ilə 1 artıraraq dəqiqləşdiririk.
Əgər a \ne x~(b \ne y), bu halda dəyəri daha ötürülməlidir.
Əgər x \ge m + 1 (sorğu intervalı tamamilə sağ alt ağacda yerləşirsə), bu halda sol övlad üçün dəyəri 0 olaraq qalır.
Tutaq ki, sorğu q(x, y) [a_1, b_1], [a_2, b_2], ..., [a_k, b_k] əsas segmentləri ilə örtülür. Bu halda [a_i, b_i] segmenti üçün dəyərini
müəyyən edirik.
Məsələn, ağac intervalında qurulubsa və sorğu q(2, 7) aşağıdakı əsas segmentləri örtürsə:
Nümunə
Növbəti testi nəzərdən keçirək:
5
5 3 2 4 5
2
2 2 5
1 1 3Hər bir zirvənin yaxınında düzbucaqda intervalın sum resm halı yazılmışdır.
Alqoritmin realizasiyası
Dəmir ağacı saxlamaq üçün SegTree massivini elan edək.
struct node
{
long long summa;
int left;
};
vector<node> SegTree;BuildTree funksiyası xətti ağacı qurur.
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 funksiyası ilə arasındaki rəqəmlərin cəmini hesablayır.
long long Sum(int a, int b)
{
return 1LL * (a + b) * (b - a + 1) / 2;
}Push funksiyası zirvəsindən onun oğullarına dəyərini ötürür. Oğullar üçün yenidən hesablanır.
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 funksiyası [left; right] intervallını, from, from + 1, from + 2, ... ardıcıllığı ilə doldurur.
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;Əgər sorğu intervalı [mid + 1; rpos] sağ alt ağacda tamamilə yerləşirsə, sağ oğula dəyərini dəqiq dəyişiklik etmədən ötürürük (bu halda 0 olaraq təyin edilir).
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 funksiyası [left; right] intervalında rəqəmlərin cəmini qaytarır.
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);
}Proqramın əsas hissəsi. Giriş rəqəmlər massivini oxuyuruq.
scanf("%d", &n);
a.resize(n + 1);
for(i = 1; i <= n; ++i)
scanf("%d", &a[i]);Xətti ağacı qururuq.
SegTree.resize(4 * n);
BuildTree(1, 1, n);sorğularını ardıcıl olaraq işləyirik.
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);
}