-| **A Dizisi** | $4$ | $6$ | $3$ | $12$ | $1$ |
-|-------------------:|:---:|:-----:|:-------:|:----------:|:------------:|
-| **Prefix Sum Dizisi** | $4$ | $10$ | $13$ | $25$ | $26$ |
-| | $4$ | $4+6$ | $4+6+3$ | $4+6+3+12$ | $4+6+3+12+1$ |
-
-
-Prefix sum dizisini kullanarak herhangi bir $[l,r]$ aralığındaki elemanların toplamını şu şekilde kolaylıkla elde edebiliriz:
-
-$$sum_r = \sum_{j=1}^{r} {a_j}$$
-
-$$sum_{l - 1} = \sum_{j=1}^{l - 1} {a_j}$$
-
-$$sum_r - sum_{l-1} = \sum_{j=l}^{r} {a_j}$$
-
-### Örnek Kod Parçaları
-
-Prefix Sum dizisini kurarken $sum_i = sum_{i - 1} + a_i$ eşitliği kolayca görülebilir ve bu eşitliği kullanarak $sum[]$ dizisini girdi dizisindeki elemanları sırayla gezerek kurabiliriz:
-
-```c++
-const int n;
-int sum[n + 1], a[n + 1];
-// a dizisi girdi dizimiz, sum dizisi de prefix sum dizimiz olsun.
-
-void build() {
- for (int i = 1; i <= n; i++)
- sum[i] = sum[i - 1] + a[i];
- return;
-}
-
-int query(int l, int r) {
- return sum[r] - sum[l - 1];
-}
-```
-
-### Zaman Karmaşıklığı
-
-Prefix sum dizisini kurma işlemimizin zaman ve hafıza karmaşıklığı $\mathcal{O}(N)$. Her sorguya da $\mathcal{O}(1)$ karmaşıklıkta cevap verebiliyoruz.
-
-Prefix sum veri yapısı ile ilgili örnek bir probleme [buradan](https://codeforces.com/problemset/problem/816/B){target="_blank"} ulaşabilirsiniz.
-
-## Sparse Table
-
-Sparse table aralıklardaki elemanların toplamı, minimumu, maksimumu ve EBOB'ları gibi sorgulara $\mathcal{O}(\log N)$ zaman karmaşıklığında cevap alabilmemizi sağlayan bir veri yapısıdır. Bazı tip sorgular (aralıktaki minimum, maksimum sayıyı bulma gibi) ise $\mathcal{O}(1)$ zaman karmaşıklığında yapmaya uygundur.
-
-Bu veri yapısı durumu değişmeyen, sabit bir veri üzerinde ön işlemler yaparak kurulur. Dinamik veriler için kullanışlı değildir. Veri üzerinde herhangi bir değişiklik durumda Sparse table tekrardan kurulmalıdır. Bu da maliyetli bir durumdur.
-
-### Yapısı ve Kuruluşu
-
-Sparse table iki bouyutlu bir dizi şeklinde, $\mathcal{O}(N\log N)$ hafıza karmaşıklığına sahip bir veri yapısıdır. Dizinin her elemanından $2$'nin kuvvetleri uzaklıktaki elemanlara kadar olan cevaplar Sparse table'da saklanır. $ST_{x,i}$, $x$ indeksli elemandan $x + 2^i - 1$ indeksli elemana kadar olan aralığın cevabını saklayacak şekilde sparse table kurulur.
-
-```c++
-// Toplam sorgusu icin kurulmus Sparse Table Yapisi
-const int n;
-const int LOG = log2(n);
-int a[n + 1], ST[2 * n][LOG + 1];
-
-void build() {
- for (int i = 1; i <= n; i++) {
- // [i,i] araliginin cevabi dizinin i indeksli elemanina esittir.
- ST[i][0] = a[i];
- }
-
- for (int i = 1; i <= LOG; i++)
- for (int j = 1; j <= n; j++) {
- // [i,i+2^(j)-1] araliginin cevabi
- // [i,i+2^(j - 1) - 1] araligi ile [i+2^(j - 1),i+2^j-1] araliginin
- // cevaplarinin birlesmesiyle elde edilir
- ST[i][j] = ST[i][j - 1] + ST[i + (1 << (j - 1))][j - 1];
- }
-
- return;
-}
-```
-
-### Sorgu Algoritması
-
-Herhangi bir $[l,r]$ aralığı için sorgu algoritması sırasıyla şu şekilde çalışır:
-
-- $[l,r]$ aralığını cevaplarını önceden hesapladığımız aralıklara parçala.
- - Sadece $2$'nin kuvveti uzunluğunda parçaların cevaplarını sakladığımız için aralığımızı $2$'nin kuvveti uzunluğunda aralıklara ayırmalıyız. $[l,r]$ aralığının uzunluğunun ikilik tabanda yazdığımızda hangi aralıklara parçalamamız gerektiğini bulmuş oluruz.
-- Bu aralıklardan gelen cevapları birleştirerek $[l,r]$ aralığının cevabını hesapla.
-
-Herhangi bir aralığın uzunluğunun ikilik tabandaki yazılışındaki $1$ rakamlarının sayısı en fazla $\log(N)$ olabileceğinden parçalayacağımız aralık sayısı da en fazla $\log(N)$ olur. Dolayısıyla sorgu işlemimiz $\mathcal{O}(\log N)$ zaman karmaşıklığında çalışır.
-
-Örneğin: $[4,17]$ aralığının cevabını hesaplamak için algoritmamız $[4,17]$ aralığını $[4,11]$, $[12,15]$ ve $[16,17]$ aralıklarına ayırır ve bu $3$ aralıktan gelen cevapları birleştirerek istenilen cevabı hesaplar.
-
-```c++
-// toplam sorgusu
-int query(int l, int r) {
- int res = 0;
-
- for (int i = LOG; i >= 0; i--) {
- // her seferinde uzunlugu r - l + 1 gecmeyecek
- // en buyuk araligin cevabi ekleyip l'i o araligin sonuna cekiyoruz.
- if (l + (1 << i) <= r) {
- res += ST[l][i];
- l += (1 << i);
- }
- }
-
- return res;
-}
-```
-
-### Minimum ve Maksimum Sorgu
-
-Sparse Table veri yapısının diğer veri yapılarından farklı olarak $\mathcal{O}(1)$ zaman karmaşıklığında aralıklarda minimum veya maksimum sorgusu yapabilmesi en avantajlı özelliğidir.
-
-Herhangi bir aralığın cevabını hesaplarken bu aralıktaki herhangi bir elemanı birden fazla kez değerlendirmemiz cevabı etkilemez. Bu durum aralığımızı $2$'nin kuvveti uzunluğunda maksimum $2$ adet aralığa bölebilmemize ve bu aralıkların cevaplarını $\mathcal{O}(1)$ zaman karmaşıklığında birleştirebilmemize olanak sağlar.
-
-```c++
-int RMQ(int l, int r) {
- // log[] dizisinde her sayinin onceden hesapadigimiz log2 degerleri saklidir.
- int j = log[r - l + 1];
- return min(ST[l][j], ST[r - (1 << j) + 1][j]);
-}
-```
-
-Sparse Table veri yapısı ile ilgili örnek bir probleme [buradan](https://www.spoj.com/problems/RMQSQ){target="_blank"} ulaşabilirsiniz.
-
-## Binary Indexed Tree
-
-Fenwick Tree olarak da bilinen Binary Indexed Tree, [Prefix Sum](#prefix-sum) ve [Sparse Table](#sparse-table) yapılarına benzer bir yapıda olup dizi üzerinde değişiklik yapabilmemize olanak sağlayan bir veri yapısıdır. Fenwick Tree'nin diğer veri yapılarına göre en büyük avantajı pratikte daha hızlı olması ve hafıza karmaşıklığının $\mathcal{O}(N)$ olmasıdır. Ancak Fenwick Tree'de sadece prefix cevapları (veya suffix cevapları) saklayabildiğimizden aralıklarda minimum, maksimum ve EBOB gibi bazı sorguların cevaplarını elde edemeyiz.
-
-### Yapısı ve Kuruluşu
-
-$g(x)$, $x$ sayısının bit gösteriminde yalnızca en sağdaki bitin 1 olduğu tam sayı olsun. Örneğin $20$'nin bit gösterimi $(10100)_2$ olduğundan $g(20)=4$'tür. Çünkü ilk kez sağdan $3.$ bit $1$'dir ve $(00100)_2=4$'tür. Fenwick Tree'nin $x$ indeksli düğümünde,
-
-
-
- Blokların Cevapları |
- $21$ |
- $13$ |
- $50$ |
- $32$ |
-
-
-
-
- Dizideki Elemanlar |
- $3$ |
- $6$ |
- $2$ |
- $10$ |
- $3$ |
- $1$ |
- $4$ |
- $5$ |
- $2$ |
- $7$ |
- $37$ |
- $4$ |
- $11$ |
- $6$ |
- $8$ |
- $7$ |
-
-
- Elemanların İndeksleri |
- $1$ |
- $2$ |
- $3$ |
- $4$ |
- $5$ |
- $6$ |
- $7$ |
- $8$ |
- $9$ |
- $10$ |
- $11$ |
- $12$ |
- $13$ |
- $14$ |
- $15$ |
- $16$ |
-
-
-
-
- *Örnek dizideki $[3,13]$ aralığının cevabını $2.$ ve $3.$ blokların cevapları ile $3,4$ ve $11$ indeksli elemanların toplanmasıyla elde edilir.*
-
-
-```c++
-// [l,r] araligindaki elemanlarin toplamini hesaplayan fonksiyon.
-int query(int l, int r) {
- int res = 0;
-
- if (wh[l] == wh[r]) { // l ve r ayni blogun icindeyse
- for (int i = l; i <= r; i++)
- res += a[i];
- } else {
- for (int i = wh[l] + 1; i <= wh[r] - 1; i++)
- res += sum[i]; // tamamen kapladigimiz bloklarin cevaplarini ekliyoruz.
-
- // tamamen kaplamadigimiz bloklardaki araligimiz icindeki
- // elemanlarin cevaplarini ekliyoruz.
-
- for (int i = st[wh[l]]; i <= fn[wh[l]]; i++)
- if (i >= l && i <= r)
- res += a[i];
-
- for (int i = st[wh[r]]; i <= fn[wh[r]]; i++)
- if (i >= l && i <= r)
- res += a[i];
- }
-
- return res;
-}
-```
-
-### Eleman Güncelleme Algoritması
-
-Herhangi bir elemanın değerini güncellerken o elemanı içeren blokun değerini güncellememiz yeterli olacaktır. Dolayısıyla güncelleme işlemimimiz $\mathcal{O}(1)$ zaman karmaşıklığında çalışır.
-
-```c++
-void update(int x, int val) {
- // x indeksli elemanin yeni degerini val degerine esitliyoruz.
- sum[wh[x]] -= a[x];
- a[x] = val;
- sum[wh[x]] += a[x];
-}
-```
-
-SQRT Decomposition veri yapısı ile ilgili örnek bir probleme [buradan](https://codeforces.com/contest/13/problem/E){target="_blank"} ulaşabilirsiniz.
-
-## Segment Tree
-
-Segment Tree bir dizide $\mathcal{O}(\log N)$ zaman karmaşıklığında herhangi bir $[l,r]$ aralığı icin minimum, maksimum, toplam gibi sorgulara cevap verebilmemize ve bu aralıklar üzerinde güncelleme yapabilmemize olanak sağlayan bir veri yapısıdır.
-
-Segment Tree, [Fenwick Tree](#binary-indexed-tree) ve [Sparse Table](#sparse-table) yapılarından farklı olarak elemanlar üzerinde güncelleme yapılabilmesi ve minimum, maksimum gibi sorgulara da olanak sağlaması yönünden daha kullanışlıdır. Ayrıca Segment Tree $\mathcal{O}(N)$ hafıza karmaşıklığına sahipken Sparse Table yapısında gereken hafıza karmaşıklığı $\mathcal{O}(N \log N)$'dir.
-
-### Yapısı ve Kuruluşu
-Segment Tree, "Complete Binary Tree" yapısına sahiptir. Segment Tree'nin yaprak düğümlerinde dizinin elemanları saklıdır ve bu düğümlerin atası olan her düğüm kendi çocuğu olan düğümlerinin cevaplarının birleşmesiyle oluşur. Bu sayede her düğümde belirli aralıkların cevapları ve root düğümünde tüm dizinin cevabı saklanır. Örneğin toplam sorgusu için kurulmuş bir Segment Tree yapısı için her düğümün değeri çocuklarının değerleri toplamına eşittir.
-
-