Toplam#2307

Toplam (Həll təhlili)

Bütün elementlərə təyin etmə və toplama qrup əməliyyatlarını dəstəkləyən seqment ağacını həyata keçirmək lazımdır.

Hər ağac düyünündə olan dəyişənində bu düyünə aid seqmentin bütün elementlərinə təyin edilmiş dəyər saxlanılır. Əgər ağacın zirvəsi seqmentə uyğun gəlirsə [l; r], bu seqmentdəki ədədlərin cəmi add \cdot (r - l + 1)-yə bərabərdir.

Təyin etmə və toplama əməliyyatları yerinə yetirilərkən, ağacın kökündən yarpaqlara enən prosesdə hər zirvədə dəyəri ağacın növbəti səviyyəsinə basdırılmalıdır.

Təyin etmə əməliyyatı yerinə yetirilərkən s_1 və s_2 alt zirvələrindəki əvvəlki cəm dəyərləri ilə a_1 və a_2 təxirə salınmış təyin etmə dəyərləri itirilir.

Seqment ağacı qurularkən, hər zirvədəki dəyişənini seqment elementlərinə heç vaxt təyin edilməyəcək bir dəyər ilə (məsələn, ) başlatırıq.

Alqoritmin həyata keçirilməsi

Seqment ağacı tipinə malik array kimi saxlanılır. Hər ağac zirvəsi arraydə müəyyən seqmentə uyğun gəlir və bu seqment haqqında məlumat saxlayır.

Struktorda iki sahə var:

Konstruktor hər ağac zirvəsi yaradılarkən avtomatik olaraq çağırılır:

struct node
{
  long long sum, add;
  node() : sum(0), add(-1) {}
};
vector<node> SegTree;

Push funksiyası zirvəsindən onun oğullarına dəyərini basdırır. Əgər add \ne -1-dirsə, bu dəyəri ağacın bir səviyyə aşağıya basdırmaq lazımdır. Basdırma bitdikdən sonra zirvəsindəki dəyərini -lə əvəz edirik.

void Push(int v, int lpos, int mid, int rpos)
{

Əgər add = -1-dirsə, zirvəsində təxirə salınmış əməliyyat yoxdur.

  if (SegTree[v].add == -1) return;

zirvəsinin oğullarında və dəyərlərinin yenidən hesablanması.

  SegTree[2*v].add = SegTree[v].add;
  SegTree[2*v].sum = (mid - lpos + 1) * SegTree[v].add;
  SegTree[2*v+1].add = SegTree[v].add;
  SegTree[2*v+1].sum = (rpos - mid) * SegTree[v].add;

Basdırma bitdikdən sonra add = -1-lə zirvəsində təyin edilir.

  SegTree[v].add = -1;
}

SetValue funksiyası [left; right] seqmentindəki bütün elementləri -ə bərabər təyin edir. zirvəsinə uyğun gələn seqment [lpos; rpos].

void SetValue(int v, int lpos, int rpos, int left, int right, int val)
{
    if (left > right) return;

Əgər [left; right] [lpos; rpos] seqmentinə uyğun gəlirsə, hansı ki ağac zirvəsində saxlanılır, bu zirvədə və dəyişənlərinə müvafiq dəyərlər təyin edirik.

  if ((lpos == left) && (rpos == right))
  {

    SegTree[v].add = val;
    SegTree[v].sum = (long long)(right - left + 1) * val;
    return;
  }

Seqmentin ortasını tapırıq mid = (lpos + rpos) / 2.

  int mid = (lpos + rpos) / 2;

Basdırmanı həyata keçiririk, əgər -lə bərabərdirsə.

  Push(v, lpos, mid, rpos);

Seqment ağacı zirvəsinin sol və sağ oğullarını rekursiv olaraq işləyirik.

  SetValue(2*v, lpos, mid, left, min(mid, right), val);
  SetValue(2*v+1, mid+1, rpos, max(left, mid+1), right, val);

zirvəsində cəm dəyərini onun uşaqların cəm dəyərləri ilə yenidən hesablayırıq.

  SegTree[v].sum = SegTree[2*v].sum + SegTree[2*v+1].sum;
}

Summa funksiyası [left; right] seqmentindəki elementlərin cəmini geri qaytarır.

zirvəsinə uyğun gələn seqment [lpos; rpos].

long long Summa(int v, int lpos, int rpos, int left, int right)
{
  if (left > right) return 0;

Əgər [left; right] aghac zirvəsində saxlanan [lpos; rpos] seqmenti ilə üst-üstə düşərsə, bu zirvədəki cəm dəyərini qaytarırıq.

  if ((lpos == left) && (rpos == right)) 
    return SegTree[v].sum;

Seqmentin ortasını tapırıq mid = (lpos + rpos) / 2.

  int mid = (lpos + rpos) / 2;

Basdırmanı yerinə yetiririk, -dən fərqlidirsə.

  Push(v, lpos, mid, rpos);

Seqmenti [lpos; rpos] iki alt seqmentə bölürük: [lpos; mid] və [mid + 1; rpos]. Bu alt seqmentlərdə cəm hesabatı edir və əldə edilən dəyərləri topluyuruq.

  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ş verilənlərini oxuyuruq. Seqment ağacını başlatırıq.

cin >> n >> k;
SegTree.resize(4 * n);

sorğuları işləyirik. Sorğuların növündən asılı olaraq, ya qrup təyin etməni, ya da verilmiş seqmentdə cəmin hesablanmasını yerinə yetiririk.

while (k--)
{
  cin >> c;
  if (c == 'A')
  {
    cin >> l >> r >> x;
    SetValue(1, 1, n, l, r, x);
  }
  else
  {
    cin >> l >> r;
    cout << Summa(1, 1, n, l, r) << endl;
  }
}