Cây Fenwick (BIT)
Tổng tiền tố trả lời tổng đoạn bằng một phép trừ, nhưng khi dãy thay đổi (sửa một phần tử) thì phải tính lại cả mảng tiền tố, mất n bước. Đề có 105 lần sửa xen kẽ 105 câu hỏi thì cách nào cũng tới 1010 bước. Cây Fenwick (còn gọi là BIT, Binary Indexed Tree) làm cả hai việc trong khoảng log2n bước.
Ý tưởng: mỗi ô giữ tổng của một khúc
Phần tiêu đề “Ý tưởng: mỗi ô giữ tổng của một khúc”Đánh số từ 1. Ô bit[i] giữ tổng của khúc kết thúc tại i, có độ dài bằng bit 1 thấp nhất của i
(viết i dưới dạng nhị phân, lấy lũy thừa của 2 ứng với
chữ số 1 cuối cùng). Bit thấp nhất tính bằng i & -i.
| i | nhị phân | i & -i | bit[i] giữ tổng |
|---|---|---|---|
| 1 | 0001 | 1 | a1 |
| 2 | 0010 | 2 | a1 + a2 |
| 3 | 0011 | 1 | a3 |
| 4 | 0100 | 4 | a1 + … + a4 |
| 5 | 0101 | 1 | a5 |
| 6 | 0110 | 2 | a5 + a6 |
| 7 | 0111 | 1 | a7 |
| 8 | 1000 | 8 | a1 + … + a8 |
Tổng tiền tố đến i: cộng bit[i], rồi nhảy về trước khúc vừa cộng (i -= i & -i), lặp đến khi
i = 0. Ví dụ tổng đến 7: bit[7] (a7) + bit[6] (a5 + a6) + bit[4]
(a1 đến a4). Mỗi lần nhảy xóa một bit 1 của i, nên không quá log2n lần.
Cộng v vào ai: mọi ô có khúc chứa vị trí i đều phải cộng v. Đó là i, rồi
i += i & -i, lặp đến khi vượt n. Ví dụ cộng vào a3: sửa bit[3], bit[4], bit[8]. Mỗi lần
nhảy, bit thấp nhất dịch lên ít nhất một bậc, nên cũng không quá log2n lần.
import sys
def cong(i, v): # a[i] += v while i <= n: bit[i] += v i += i & -i
def tong_tien_to(i): # a[1] + ... + a[i] s = 0 while i > 0: s += bit[i] i -= i & -i return s
du_lieu = sys.stdin.read().split()n, q = int(du_lieu[0]), int(du_lieu[1])bit = [0] * (n + 1)for i in range(1, n + 1): cong(i, int(du_lieu[1 + i]))ket_qua = []vi_tri = 2 + nfor _ in range(q): loai, x, y = du_lieu[vi_tri], int(du_lieu[vi_tri + 1]), int(du_lieu[vi_tri + 2]) vi_tri += 3 if loai == '1': cong(x, y) # cộng y vào a[x] else: ket_qua.append(tong_tien_to(y) - tong_tien_to(x - 1)) # tổng từ x đến yprint("\n".join(map(str, ket_qua)))#include <iostream>#include <vector>using namespace std;
int n;vector<long long> bit;
void cong(int i, long long v) { // a[i] += v for (; i <= n; i += i & -i) bit[i] += v;}
long long tongTienTo(int i) { // a[1] + ... + a[i] long long s = 0; for (; i > 0; i -= i & -i) s += bit[i]; return s;}
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int q; cin >> n >> q; bit.assign(n + 1, 0); for (int i = 1; i <= n; i++) { long long x; cin >> x; cong(i, x); } while (q--) { int loai, x; long long y; cin >> loai >> x >> y; if (loai == 1) cong(x, y); // cộng y vào a[x] else cout << tongTienTo(y) - tongTienTo(x - 1) << "\n"; // tổng từ x đến y } return 0;}Các cách dùng hay gặp
Phần tiêu đề “Các cách dùng hay gặp”| Bài toán | Cách dùng Fenwick |
|---|---|
| sửa một phần tử, hỏi tổng đoạn | như chương trình trên |
| cộng v vào cả đoạn [l, r], hỏi một phần tử | Fenwick trên mảng hiệu: cộng v tại l, trừ v tại r + 1; giá trị ai là tổng tiền tố đến i |
| đếm số phần tử nhỏ hơn x đã gặp | Fenwick trên giá trị: gặp x thì cộng 1 vào ô x; số phần tử nhỏ hơn x là tổng tiền tố đến x - 1 |
| giá trị lớn tới 109 | rời rạc hóa trước |
Đếm nghịch thế
Phần tiêu đề “Đếm nghịch thế”Nghịch thế là cặp i đứng trước j mà ai lớn hơn aj. Đi từ trái sang phải, trước khi thêm aj vào Fenwick (theo giá trị), đếm số phần tử đã thêm lớn hơn aj: bằng (số đã thêm) - (tổng tiền tố đến aj).
Dãy 3 1 4 2:
| aj | đã thêm | lớn hơn aj | nghịch thế cộng dồn |
|---|---|---|---|
| 3 | 0 | 0 | |
| 1 | 3 | 1 | 1 |
| 4 | 3 1 | 0 | 1 |
| 2 | 3 1 4 | 2 (là 3 và 4) | 3 |
Cả bài khoảng n × log2n bước, so với n2 / 2 cặp nếu thử hết.
Luyện tập
Phần tiêu đề “Luyện tập”Gồm 20 bài, chia thành 5 nhóm từ dễ đến khó. Mỗi bài có một dữ liệu kiểm tra: các bạn chạy chương trình của mình với dữ liệu đó rồi so với đáp số.
Nhóm 1. Sửa phần tử, hỏi tổng
Phần tiêu đề “Nhóm 1. Sửa phần tử, hỏi tổng”Bài 1. Cộng một phần tử, tổng đoạn
Phần tiêu đề “Bài 1. Cộng một phần tử, tổng đoạn”Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), dòng thứ hai có n số nguyên (|ai| ≤ 109), q dòng sau mỗi dòng là 1 i v (cộng v vào ai, |v| ≤ 109) hoặc 2 l r (hỏi tổng al + … + ar). In ra đáp số các câu hỏi loại 2.
Ví dụ:
| Nhập | In ra |
|---|---|
5 51 2 3 4 52 1 31 2 102 1 32 3 51 5 -5 | 61612 |
Kiểm tra: nhập
8 64 -2 7 0 3 3 -8 12 2 61 4 52 1 81 1 -42 1 42 8 8Bài 2. Sửa giá trị, tổng đoạn
Phần tiêu đề “Bài 2. Sửa giá trị, tổng đoạn”Giống bài 1, nhưng thao tác loại 1 là 1 i v: gán ai = v (thay vì cộng). Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), dòng thứ hai có n số, q dòng sau là các thao tác. In ra đáp số các câu hỏi loại 2.
Ví dụ:
| Nhập | In ra |
|---|---|
5 41 2 3 4 51 3 102 1 51 3 02 2 4 | 226 |
Kiểm tra: nhập
6 65 5 5 5 5 52 1 61 6 -51 1 -52 1 61 6 202 4 6Bài 3. Cộng đoạn, hỏi một phần tử
Phần tiêu đề “Bài 3. Cộng đoạn, hỏi một phần tử”Dãy n số ban đầu toàn 0. Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), q dòng sau mỗi dòng là 1 l r v (cộng v vào mọi al, …, ar, |v| ≤ 109) hoặc 2 i (hỏi ai). In ra đáp số các câu hỏi loại 2.
Ví dụ:
| Nhập | In ra |
|---|---|
5 51 2 4 32 31 1 2 -12 22 5 | 320 |
Kiểm tra: nhập
8 71 1 8 21 3 5 42 41 4 4 -102 42 62 1Bài 4. Cộng đoạn, tổng đoạn
Phần tiêu đề “Bài 4. Cộng đoạn, tổng đoạn”Dãy n số ban đầu toàn 0. Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), q dòng sau mỗi dòng là 1 l r v (cộng v vào mọi al, …, ar, |v| ≤ 106) hoặc 2 l r (hỏi tổng al + … + ar). In ra đáp số các câu hỏi loại 2.
Ví dụ:
| Nhập | In ra |
|---|---|
5 41 1 3 22 2 51 2 5 12 1 5 | 410 |
Kiểm tra: nhập
8 61 1 8 11 4 6 32 1 32 4 81 2 2 -72 1 8Bài 5. Công tắc đèn
Phần tiêu đề “Bài 5. Công tắc đèn”Có n bóng đèn, ban đầu đều tắt. Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), q dòng sau mỗi dòng là 1 l r (bấm công tắc mọi bóng từ l đến r: bóng đang tắt thì bật, đang bật thì tắt) hoặc 2 i (hỏi bóng i đang bật hay tắt). Với câu hỏi loại 2, in ra 1 nếu bật, 0 nếu tắt.
Ví dụ:
| Nhập | In ra |
|---|---|
5 51 1 32 21 2 52 22 5 | 101 |
Kiểm tra: nhập
8 71 1 81 3 62 41 4 42 42 82 2Nhóm 2. Đếm bằng Fenwick trên giá trị
Phần tiêu đề “Nhóm 2. Đếm bằng Fenwick trên giá trị”Bài 6. Đếm nghịch thế
Phần tiêu đề “Bài 6. Đếm nghịch thế”Dòng đầu có n (1 ≤ n ≤ 2 × 105), dòng thứ hai có n số nguyên (|ai| ≤ 109). In ra số cặp vị trí i đứng trước j mà ai lớn hơn aj.
Ví dụ:
| Nhập | In ra |
|---|---|
43 1 4 2 | 3 |
Kiểm tra: nhập
101000000000 -5 7 7 -1000000000 0 3 3 12 -5Bài 7. Số nhỏ hơn ở bên phải
Phần tiêu đề “Bài 7. Số nhỏ hơn ở bên phải”Dòng đầu có n (1 ≤ n ≤ 2 × 105), dòng thứ hai có n số nguyên (|ai| ≤ 109). Với mỗi vị trí i, in ra số phần tử đứng sau i và nhỏ hơn ai.
Ví dụ:
| Nhập | In ra |
|---|---|
55 2 6 1 3 | 3 1 2 0 0 |
Kiểm tra: nhập
104 -1 4 9 0 2 -7 9 3 1Bài 8. Bộ ba tăng dần
Phần tiêu đề “Bài 8. Bộ ba tăng dần”Dòng đầu có n (1 ≤ n ≤ 2 × 105), dòng thứ hai có n số nguyên (|ai| ≤ 109). In ra số bộ ba vị trí i đứng trước j, j đứng trước k mà ai nhỏ hơn aj và aj nhỏ hơn ak.
Ví dụ:
| Nhập | In ra |
|---|---|
51 3 2 4 5 | 7 |
Kiểm tra: nhập
105 1 7 3 3 8 2 9 6 10Bài 9. Thứ hạng ngay khi thi xong
Phần tiêu đề “Bài 9. Thứ hạng ngay khi thi xong”n bạn lần lượt thi, bạn thứ i được điểm si. Ngay khi bạn i thi xong, xếp hạng bạn i trong số i bạn đã thi: hạng bằng 1 cộng số bạn đã thi có điểm cao hơn. Dòng đầu có n (1 ≤ n ≤ 2 × 105), dòng thứ hai có n số si (0 ≤ si ≤ 109). In ra hạng của từng bạn ngay khi thi xong.
Ví dụ:
| Nhập | In ra |
|---|---|
570 90 80 90 60 | 1 1 2 1 5 |
Kiểm tra: nhập
10500 300 500 800 100 800 700 0 900 500Bài 10. Đoạn có trung bình ít nhất k
Phần tiêu đề “Bài 10. Đoạn có trung bình ít nhất k”Dòng đầu có n, k (1 ≤ n ≤ 2 × 105, |k| ≤ 109), dòng thứ hai có n số nguyên (|ai| ≤ 109). In ra số đoạn liên tiếp có trung bình cộng ít nhất k.
Ví dụ:
| Nhập | In ra |
|---|---|
4 31 5 2 4 | 7 |
Kiểm tra: nhập
10 03 -4 2 -1 0 5 -6 1 1 -2Bài 11. Đổi chỗ kề nhau để biến xâu
Phần tiêu đề “Bài 11. Đổi chỗ kề nhau để biến xâu”Nhập hai xâu s và t cùng độ dài (1 ≤ độ dài ≤ 2 × 105), gồm chữ cái thường, t là một cách sắp xếp lại các chữ cái của s. Mỗi bước được đổi chỗ hai ký tự kề nhau của s. In ra số bước ít nhất để biến s thành t.
Ví dụ:
| Nhập | In ra |
|---|---|
abccab | 2 |
Kiểm tra: nhập
bumbiiimbuibNhóm 3. Tìm phần tử thứ k
Phần tiêu đề “Nhóm 3. Tìm phần tử thứ k”Bài 12. Túi số, lấy số nhỏ thứ k
Phần tiêu đề “Bài 12. Túi số, lấy số nhỏ thứ k”Một túi ban đầu rỗng. Dòng đầu có q (1 ≤ q ≤ 2 × 105), q dòng sau mỗi dòng là 1 x (bỏ số x vào túi, 1 ≤ x ≤ 106), 2 x (lấy một số x ra khỏi túi, chắc chắn túi đang có x) hoặc 3 k (hỏi số nhỏ thứ k trong túi, chắc chắn túi có ít nhất k số). In ra đáp số các câu hỏi loại 3.
Ví dụ:
| Nhập | In ra |
|---|---|
71 51 21 83 22 51 23 2 | 52 |
Kiểm tra: nhập
91 1001 1001 73 32 1003 21 10000001 13 4Bài 13. Đếm để loại
Phần tiêu đề “Bài 13. Đếm để loại”n bạn đánh số 1 đến n đứng thành vòng tròn. Bắt đầu từ bạn 1, đếm 1, 2, …, k theo vòng; bạn được đếm số k bị loại, rồi đếm lại từ 1 bắt đầu từ bạn kế tiếp. Lặp đến khi hết người. Dòng duy nhất có n, k (1 ≤ n ≤ 2 × 105, 1 ≤ k ≤ 109). In ra thứ tự các bạn bị loại.
Ví dụ: nhập 7 3 thì in ra 3 6 2 7 5 1 4.
Kiểm tra: nhập 10 4.
Nhóm 4. Xử lý offline
Phần tiêu đề “Nhóm 4. Xử lý offline”Bài 14. Số giá trị khác nhau trong đoạn
Phần tiêu đề “Bài 14. Số giá trị khác nhau trong đoạn”Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), dòng thứ hai có n số nguyên (1 ≤ ai ≤ 109), q dòng sau mỗi dòng có l, r. Với mỗi câu hỏi, in ra số giá trị khác nhau trong đoạn al, …, ar.
Ví dụ:
| Nhập | In ra |
|---|---|
6 31 2 1 3 2 21 32 64 4 | 231 |
Kiểm tra: nhập
10 45 5 7 5 9 7 7 1 9 51 102 43 78 10Bài 15. Tổng các số không vượt quá x trong đoạn
Phần tiêu đề “Bài 15. Tổng các số không vượt quá x trong đoạn”Dòng đầu có n, q (1 ≤ n, q ≤ 2 × 105), dòng thứ hai có n số nguyên (1 ≤ ai ≤ 109), q dòng sau mỗi dòng có l, r, x. Với mỗi câu hỏi, in ra tổng các ai với l ≤ i ≤ r và ai ≤ x.
Ví dụ:
| Nhập | In ra |
|---|---|
5 34 9 2 7 51 5 52 4 83 3 1 | 1190 |
Kiểm tra: nhập
8 410 3 8 3 15 1 6 121 8 62 5 34 8 121 1 9Bài 16. Đếm điểm trong hình chữ nhật
Phần tiêu đề “Bài 16. Đếm điểm trong hình chữ nhật”Có n điểm trên mặt phẳng và q hình chữ nhật có cạnh song song với trục. Dòng đầu có n, q (1 ≤ n, q ≤ 105), n dòng sau mỗi dòng có x, y của một điểm, q dòng sau mỗi dòng có x1, y1, x2, y2 (x1 ≤ x2, y1 ≤ y2); mọi tọa độ là số nguyên có giá trị tuyệt đối không quá 109. Với mỗi hình chữ nhật, in ra số điểm nằm trong hoặc trên cạnh.
Ví dụ:
| Nhập | In ra |
|---|---|
5 21 12 34 23 35 51 1 3 32 2 5 4 | 33 |
Kiểm tra: nhập
8 30 0-5 23 -13 37 7-2 -21 13 0-3 -3 3 30 0 10 10-10 1 -1 5Nhóm 5. Biến thể
Phần tiêu đề “Nhóm 5. Biến thể”Bài 17. Bảng có cập nhật
Phần tiêu đề “Bài 17. Bảng có cập nhật”Bảng m hàng, n cột ban đầu toàn 0. Dòng đầu có m, n, q (1 ≤ m, n ≤ 1000, 1 ≤ q ≤ 105), q dòng sau mỗi dòng là 1 r c v (cộng v vào ô (r, c), |v| ≤ 106) hoặc 2 r1 c1 r2 c2 (hỏi tổng hình chữ nhật từ (r1, c1) đến (r2, c2)). In ra đáp số các câu hỏi loại 2.
Ví dụ:
| Nhập | In ra |
|---|---|
3 3 41 1 1 51 2 3 22 1 1 2 32 2 2 3 3 | 72 |
Kiểm tra: nhập
4 5 61 1 1 31 4 5 -21 2 3 72 1 1 4 51 1 1 -32 1 1 2 3Bài 18. Dãy con tăng dài nhất
Phần tiêu đề “Bài 18. Dãy con tăng dài nhất”Dòng đầu có n (1 ≤ n ≤ 2 × 105), dòng thứ hai có n số nguyên (|ai| ≤ 109). In ra độ dài lớn nhất của một dãy con (không cần liên tiếp) tăng ngặt.
Ví dụ:
| Nhập | In ra |
|---|---|
83 1 4 1 5 9 2 6 | 4 |
Kiểm tra: nhập
1210 -3 7 7 2 8 -1 9 4 5 6 1Bài 19. Nghịch thế khi xoay vòng
Phần tiêu đề “Bài 19. Nghịch thế khi xoay vòng”Cho một hoán vị p của 1, 2, …, n. Mỗi lần xoay, chuyển số đầu tiên xuống cuối. Dòng đầu có n (1 ≤ n ≤ 2 × 105), dòng thứ hai có p. In ra số nghịch thế nhỏ nhất trong n dãy: dãy ban đầu và n - 1 dãy sau từng lần xoay.
Ví dụ:
| Nhập | In ra |
|---|---|
43 4 1 2 | 0 |
Kiểm tra: nhập
85 7 2 8 1 3 6 4Bài 20. Đoạn có tổng không quá S, có số âm
Phần tiêu đề “Bài 20. Đoạn có tổng không quá S, có số âm”Dòng đầu có n, S (1 ≤ n ≤ 2 × 105, |S| ≤ 1015), dòng thứ hai có n số nguyên (|ai| ≤ 109, có thể âm). In ra số đoạn liên tiếp có tổng không quá S.
Ví dụ:
| Nhập | In ra |
|---|---|
4 23 -1 2 -4 | 8 |
Kiểm tra: nhập
10 05 -3 -3 4 1 -6 2 2 -1 0