Ганц array-гээр эхний r тооны нийлбэрийг олж, тоо нэмэхийг хурдан хийдэг богинохон бүтэц.
Эхлээд жижиг бодлого
a = [3, 1, 4, 1, 5] (index 1-ээс). Fenwick Tree-д bit = [3, 4, 4, 9, 5] болно: bit[2] = a[1] + a[2], bit[4] = a[1] + … + a[4] гэх мэт.
Эхний 5 тооны нийлбэр: bit[5] + bit[4] = 5 + 9 = 14. Эхний 3 тооны нийлбэр: bit[3] + bit[2] = 4 + 4 = 8.
Энгийн аргаас санаа руу
Энгийн арга бол асуулт бүрт эхний r тоог loop-оор нэмэх. Асуулт олон бол O(n·q) болж удаан. Prefix sum array хурдан хариулах ч нэг тоо өөрчлөгдөхөд O(n) prefix-ийг шинэчилнэ.
Fenwick Tree нь тоо бүрийг log n ширхэг сагсанд хуваарилна. bit[i] нь a[i]-гаар төгссөн, i & -i урттай хэсгийн нийлбэр. Аливаа prefix log n сагснаас бүрдэнэ. Нэг тоо log n сагсанд л ордог.
Нэр томьёо ба тэмдэглэгээ
- bit[i]
a[i - (i & -i) + 1 .. i]хэсгийн нийлбэр. Index 1-ээс эхэлнэ.- i & -i
i-г хоёртын системээр бичихэд хамгийн баруун талд байгаа 1 бит. 12 = 1100 бол 4.- add(i, x)
a[i]дээр x-ийг нэмнэ:i += i & -iгэж дээш үсэрсээр бүх сагсыг шинэчилнэ.- sum(i)
a[1] + … + a[i]-г олно:i -= i & -iгэж доош үсэрсээр сагснуудыг нэмнэ.
bit[i] нь a array-н нэг хэсгийн нийлбэрийг хадгална. Хэсгийн урт нь i & -i. Эхлээд a array-н тоо бүрийг нэг нэгээр нь bit руу нэмье.
- Space тоглуулах
- ←→ алхам
- −+ хурд
- Одоогийн тоо, нүд
- Шинэчилсэн нүд
- Энэ нүдний хэсэг
- Нийлбэрт орсон
- Хариу
Жижигхэн ч хүчтэй
Fenwick Tree ганц bit array-гээр эхний r тооны нийлбэрийг олж, тоо нэмэхийг хурдан хийдэг. Код нь маш богино тул тэмцээн дээр 2 минутад бичнэ.
Дараа нь юу болох вэ?
Оноо 0 / 00дараалсан зөв, хамгийн их 0
Animation нэг алхам дээр зогсоно. Дараа нь код юу хийх вэ? Таагаад оноо цуглуул! Нэвтрэх хэрэггүй, оноо чинь энэ төхөөрөмж дээр хадгалагдана.
Юунд хэрэгтэй вэ?
a[i] дээр тоо нэмэх, эхний r тооны нийлбэрийг олох query-д O(log n) хугацаанд хариулна. Segment Tree-гийн хийдэг ажлын ихэнхийг хэд дахин богино кодоор хийнэ.
Яаж ажилладаг вэ?
1. bit[i] нь a[i]-гаар төгссөн, i & -i урттай хэсгийн нийлбэрийг хадгална. i & -i нь i-г хоёртын системээр бичихэд хамгийн баруун талд байгаа 1 бит. 2. Нэмэх (add): bit[i] дээр x-ийг нэмээд i += i & -i гэж дээш үсэрнэ. a[i]-г агуулсан бүх нүд шинэчлэгдэнэ. 3. Нийлбэр (sum): s дээр bit[i]-г нэмээд i -= i & -i гэж доош үсэрнэ. i = 0 болмогц зогсоно. 4. Эхлээд тоо бүрийг add функцээр нэмж бэлдэнэ. a[l..r] хэсгийн нийлбэр = sum(r) − sum(l − 1). Оролт: эхний мөрөнд n ба q, дараагийн мөрөнд n тоо. Дараа нь q мөр: "1 i x" (a[i] дээр x нэмэх) эсвэл "2 i" (эхний i тооны нийлбэр). Index 1-ээс эхэлнэ.
Яагаад зөв, хэр хурдан бэ?
sum(i) нь i-н 1 битүүдийг баруунаас нь нэг нэгээр арилгадаг. Алхам бүрт авсан сагснууд давхцахгүй, зэргэлдээ байрласан хэсгүүд бөгөөд нийлээд яг [1, i] болно: 7 = 4 + 2 + 1 тул bit[7], bit[6], bit[4] нь a[7], a[5..6], a[1..4]. add(i) нь a[i]-г агуулсан сагс бүрээр яг нэг удаа дамжина.
Хоёр үйлдэл хоёулаа битийн тоогоор, өөрөөр хэлбэл ихдээ log₂ n + 1 алхам хийнэ. n = 100 000 бол 17 орчим алхам. Санах ой нь ердөө n + 1 элементтэй array.
Түгээмэл алдаа
- Index 0-ээс эхлүүлэх.
i = 0үедi & -i = 0тулaddхэзээ ч дуусахгүй. Index-ийг 1-ээс эхлүүл. a[i] = xболгох гэжadd(i, x)дуудах. Fenwick нэмдэг тулadd(i, x - a[i])гээдa[i]-г тусад нь хадгал.- Хэсгийн нийлбэрт
sum(r) - sum(l)гэж бичих. Зөв ньsum(r) - sum(l - 1). bit-ийгintболгох. Нийлбэр том болlong longхэрэгтэй.
Хязгаарын тохиолдол
sum(0): loop огт ажиллахгүй, 0 буцаана.l = 1үедsum(l - 1)зөв ажиллана.- n нь 2-ын зэрэг:
bit[n]нь бүх array-н нийлбэр. - Сөрөг x нэмэх:
addхэвээрээ ажиллана, тоо буурна.
Өөрийгөө шалга
bit[12]нь аль хэсгийн нийлбэрийг хадгалах вэ?Хариу харах
a[9..12].12 & -12 = 4тул урт нь 4.sum(13)бодоход аль нүднүүдийг нэмэх вэ?Хариу харах
bit[13],bit[12],bit[8]. 13 → 12 → 8 → 0.n = 8 бол
add(5, x)аль нүднүүдийг шинэчлэх вэ?Хариу харах
bit[5],bit[6],bit[8]. 5 → 6 → 8 → 16 > 8.
Гурван дадлага
Hint-ээ нээхээс өмнө өөрөө оролдоорой.
1.n = 8, бүх
a[i] = 1.bitarray-г бөглө.Hint харах
bit[i]ньi & -iширхэг 1-ийн нийлбэр.Хариу ба тайлбар
bit = [1, 2, 1, 4, 1, 2, 1, 8].sum(8) = bit[8] = 8.2.
a[l..r]хэсгийн нийлбэрийг буцаадагrangeSum(l, r)function бич.Hint харах
Хоёр prefix-ийн зөрүү.
Хариу ба тайлбар
return sum(r) - sum(l - 1);Хоёр дуудлага тул O(log n) хэвээр.3.Fenwick Tree ашиглан array дахь inversion-ий тоог O(n log n)-д ол. Тоонууд 1-ээс n хүртэл.
Hint харах
Зүүнээс баруун тийш явна. Өмнө нь гарсан тоонуудаас
a[i]-гаас их нь хэд вэ?Хариу ба тайлбар
iнь 0-ээс эхэлсэн байрлал гэе. Өмнө ньiтоо гарсан, тэдгээрийнsum(a[i])ньa[i]-гаас их биш. Тиймээсcnt += i - sum(a[i]), дараа ньadd(a[i], 1).[3, 1, 2, 5, 4]дээр хариу 3.
Жишээ
Оролт
8 3 5 8 6 3 2 7 2 6 2 7 1 3 4 2 7
Гаралт
33 37
Одоо өөрөө бодоод үз
Хичээл
Энэ сэдвийн хичээл удахгүй нэмэгдэнэ.