Bỏ qua để đến nội dung

Bảng thưa (Sparse Table)

Mức độ: Nâng cao

Có 105 câu hỏi “số nhỏ nhất từ vị trí l đến r là bao nhiêu?”. Tổng tiền tố trả lời tổng đoạn bằng một phép trừ, nhưng nhỏ nhất không trừ được: biết nhỏ nhất của 10 số đầu và của 3 số đầu không suy ra được nhỏ nhất của 7 số còn lại. Bảng thưa chuẩn bị trước nhỏ nhất của các đoạn có độ dài là lũy thừa của 2, rồi mỗi câu hỏi ghép từ hai đoạn như vậy.

Gọi bang[j][i] là số nhỏ nhất của đoạn dài 2j bắt đầu tại i, tức là từ ai đến ai + 2j - 1.

  • j = 0: đoạn dài 1, bang[0][i] = a[i].
  • j lớn hơn 0: đoạn dài 2j gồm hai nửa dài 2j - 1, nên bang[j][i] = min(bang[j-1][i], bang[j-1][i + 2^(j-1)]).

Dãy 5 2 4 7 1 3 6 8:

j (độ dài)i = 01234567
0 (1)52471368
1 (2)2241136
2 (4)21111
3 (8)1

Có khoảng log2n hàng, mỗi hàng n ô, chuẩn bị mất khoảng n × log2n bước (n = 105 là khoảng 1,7 × 106).

Đoạn [l, r] dài len = r - l + 1. Lấy k lớn nhất mà 2k ≤ len. Hai đoạn dài 2k, một bắt đầu tại l, một kết thúc tại r, phủ kín [l, r] (vì 2k + 2k ≥ len). Hai đoạn có thể chồng lên nhau, nhưng với phép nhỏ nhất thì một số bị xét hai lần không làm sai kết quả.

min(l, r) = min(bang[k][l], bang[k][r - 2k + 1])

Đoạn từ 1 đến 6 (2 4 7 1 3 6) dài 6, k = 2: min(bang[2][1], bang[2][3]) = min(1, 1) = 1.

import sys
du_lieu = sys.stdin.read().split()
n, q = int(du_lieu[0]), int(du_lieu[1])
a = list(map(int, du_lieu[2:2 + n]))
bang = [a]
j = 1
while (1 << j) <= n:
truoc = bang[-1]
nua = 1 << (j - 1)
bang.append([min(truoc[i], truoc[i + nua]) for i in range(n - (1 << j) + 1)])
j += 1
ket_qua = []
vi_tri = 2 + n
for _ in range(q):
l, r = int(du_lieu[vi_tri]) - 1, int(du_lieu[vi_tri + 1]) - 1 # đề đánh số từ 1
vi_tri += 2
k = (r - l + 1).bit_length() - 1 # 2^k ≤ độ dài
ket_qua.append(min(bang[k][l], bang[k][r - (1 << k) + 1]))
print("\n".join(map(str, ket_qua)))

Mỗi câu hỏi chỉ tốn một phép so sánh. Trong C++, 31 - __builtin_clz(x) là số mũ của lũy thừa 2 lớn nhất không vượt quá x; trong Python là x.bit_length() - 1.

Bảng thưa trả lời bằng hai đoạn chồng lên nhau, nên chỉ dùng cho phép toán mà xét một số hai lần không làm sai: f(x, x) = x.

Phép toánDùng được?Vì sao
nhỏ nhất, lớn nhấtđượcmin(x, x) = x
ƯCLNđượcƯCLN(x, x) = x
AND, OR theo bitđượcx AND x = x
tổng, tích, XORkhôngx + x khác x: phần chồng bị cộng hai lần

Với tổng thì dùng tổng tiền tố. Bảng thưa không cập nhật được khi dãy thay đổi; dãy thay đổi thì dùng cây Fenwick hoặc cây phân đoạn.

Ý tưởng “lưu kết quả cho các bước dài 2j” còn dùng cho câu hỏi nhảy k bước: mỗi vị trí i có một vị trí tiếp theo tiep[i], hỏi sau k bước (k tới 1018) đang ở đâu. Lưu nhay[j][i] là vị trí sau 2j bước từ i, với nhay[j][i] = nhay[j-1][nhay[j-1][i]]. Viết k dưới dạng nhị phân, với mỗi bit 1 thứ j thì nhảy 2j bước. Cách này còn gọi là binary lifting, dùng nhiều ở bài tổ tiên chung gần nhất.

Gồm 17 bài, chia thành 4 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ố.

Dòng đầu có n, q (1 ≤ n, q ≤ 105), dòng thứ hai có n số nguyên (|ai| ≤ 109), q dòng sau mỗi dòng có l, r (1 ≤ l ≤ r ≤ n). Với mỗi câu hỏi, in ra số nhỏ nhất từ al đến ar.

Ví dụ:

NhậpIn ra
8 3
5 2 4 7 1 3 6 8
2 7
3 4
6 8
1
4
3

Kiểm tra: nhập

10 4
9 -3 7 7 0 12 -5 4 4 1
1 3
4 6
1 10
8 10
Xem đáp số
-3
0
-5
1

Hướng dẫn giải chi tiết: liên hệ tác giả.

Một trạm đo ghi mực nước sông trong n ngày. Dòng đầu có n, q (1 ≤ n, q ≤ 105), dòng thứ hai có n số (0 ≤ mực nước ≤ 109), q dòng sau mỗi dòng có l, r. Với mỗi câu hỏi, in ra mực nước cao nhất từ ngày l đến ngày r.

Ví dụ:

NhậpIn ra
7 3
12 15 11 18 14 13 16
1 3
2 6
5 7
15
18
16

Kiểm tra: nhập

10 4
300 120 450 450 90 610 200 610 50 400
1 2
3 5
6 8
9 10
Xem đáp số
300
450
610
400

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, q (1 ≤ n, q ≤ 105), dòng thứ hai có nhiệt độ n ngày (|ti| ≤ 100), q dòng sau mỗi dòng có l, r. Với mỗi câu hỏi, in ra (nhiệt độ cao nhất) - (nhiệt độ thấp nhất) từ ngày l đến ngày r.

Ví dụ:

NhậpIn ra
6 3
20 25 18 30 22 19
1 3
2 5
4 4
7
12
0

Kiểm tra: nhập

10 4
-5 3 -12 8 8 0 -1 15 -20 7
1 4
5 7
1 10
8 9
Xem đáp số
20
9
35
35

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, q (1 ≤ n, q ≤ 105), dòng thứ hai có n số nguyên dương (ai ≤ 109), q dòng sau mỗi dòng có l, r. Với mỗi câu hỏi, in ra ƯCLN của al, …, ar.

Ví dụ:

NhậpIn ra
6 3
12 18 24 9 27 6
1 3
3 5
4 6
6
3
3

Kiểm tra: nhập

10 4
100 75 50 125 30 45 60 7 14 21
1 4
2 6
8 10
1 10
Xem đáp số
25
5
7
1

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, q (1 ≤ n, q ≤ 105), dòng thứ hai có n số nguyên (0 ≤ ai nhỏ hơn 230), q dòng sau mỗi dòng có l, r. Với mỗi câu hỏi, in ra trên một dòng hai số: AND và OR của al, …, ar.

Ví dụ:

NhậpIn ra
5 3
12 10 14 7 15
1 2
2 4
1 5
8 14
2 15
0 15

Kiểm tra: nhập

8 4
255 254 127 128 64 192 3 1
1 3
4 6
6 8
1 8
Xem đáp số
126 255
0 192
0 195
0 255

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, q (1 ≤ n, q ≤ 105), dòng thứ hai có n số nguyên (|ai| ≤ 109), q dòng sau mỗi dòng có l, r. Với mỗi câu hỏi, in ra vị trí của số nhỏ nhất trong đoạn từ l đến r (đánh số từ 1); có nhiều vị trí thì in vị trí nhỏ nhất.

Ví dụ:

NhậpIn ra
7 3
4 2 6 2 5 1 3
1 5
3 5
1 7
2
4
6

Kiểm tra: nhập

10 4
7 3 9 3 3 8 1 6 1 2
1 4
2 5
6 10
3 3
Xem đáp số
2
2
7
3

Hướng dẫn giải chi tiết: liên hệ tác giả.

Đường đi bộ có n cột mốc, độ cao cột thứ i là hi. Độ dốc giữa hai cột liền nhau là |hi + 1 - hi|. Dòng đầu có n, q (2 ≤ n ≤ 105, 1 ≤ q ≤ 105), dòng thứ hai có n số hi (0 ≤ hi ≤ 109), q dòng sau mỗi dòng có l, r (1 ≤ l nhỏ hơn r ≤ n). Với mỗi câu hỏi, in ra độ dốc lớn nhất khi đi từ cột l đến cột r.

Ví dụ:

NhậpIn ra
6 3
10 13 9 9 20 18
1 3
3 4
1 6
4
0
11

Kiểm tra: nhập

10 4
100 90 95 150 140 141 300 290 285 300
1 3
3 6
6 8
1 10
Xem đáp số
10
55
159
159

Hướng dẫn giải chi tiết: liên hệ tác giả.

n số xếp thành vòng tròn, sau số thứ n là số thứ 1. Dòng đầu có n, q (1 ≤ n, q ≤ 105), dòng thứ hai có n số nguyên (|ai| ≤ 109), q dòng sau mỗi dòng có l, r (1 ≤ l, r ≤ n). Với mỗi câu hỏi, đi theo vòng từ vị trí l đến vị trí r (nếu l lớn hơn r thì đi qua vị trí n rồi về 1), in ra số nhỏ nhất đã đi qua.

Ví dụ:

NhậpIn ra
6 3
5 9 2 7 4 8
2 4
5 1
6 6
2
4
8

Kiểm tra: nhập

8 4
3 -1 4 1 -5 9 2 6
7 2
3 6
8 1
1 8
Xem đáp số
-1
-5
3
-5

Hướng dẫn giải chi tiết: liên hệ tác giả.

Bài 9. Số chia hết cho mọi số trong đoạn

Phần tiêu đề “Bài 9. Số chia hết cho mọi số trong đoạn”

Dòng đầu có n, q (1 ≤ n, q ≤ 105), dòng thứ hai có n số nguyên dương (ai ≤ 109), q dòng sau mỗi dòng có l, r. Với mỗi câu hỏi, in ra có bao nhiêu vị trí i trong đoạn từ l đến r mà mọi số trong đoạn đều chia hết cho ai.

Ví dụ:

NhậpIn ra
5 3
2 4 2 6 3
1 4
2 5
2 2
2
0
1

Kiểm tra: nhập

10 4
6 12 6 18 6 3 9 3 3 27
1 5
1 10
6 9
2 4
Xem đáp số
3
3
3
1

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n (1 ≤ n ≤ 105), dòng thứ hai có n số nguyên dương (ai ≤ 109). In ra số đoạn liên tiếp có ƯCLN các phần tử bằng 1.

Ví dụ:

NhậpIn ra
4
2 3 4 6
5

Kiểm tra: nhập

10
6 10 15 4 8 9 12 7 14 21
Xem đáp số

38

Hướng dẫn giải chi tiết: liên hệ tác giả.

Bài 11. Đoạn dài nhất có ƯCLN lớn hơn 1

Phần tiêu đề “Bài 11. Đoạn dài nhất có ƯCLN lớn hơn 1”

Dòng đầu có n (1 ≤ n ≤ 105), dòng thứ hai có n số nguyên dương (ai ≤ 109). In ra độ dài lớn nhất của một đoạn liên tiếp có ƯCLN các phần tử lớn hơn 1, hoặc 0 nếu không có.

Ví dụ:

NhậpIn ra
6
3 6 4 8 12 5
4

Kiểm tra: nhập

10
7 14 21 2 6 9 15 25 10 1
Xem đáp số

3

Hướng dẫn giải chi tiết: liên hệ tác giả.

Bài 12. Đoạn ngắn nhất có ƯCLN bằng ƯCLN cả dãy

Phần tiêu đề “Bài 12. Đoạn ngắn nhất có ƯCLN bằng ƯCLN cả dãy”

Dòng đầu có n (1 ≤ n ≤ 105), dòng thứ hai có n số nguyên dương (ai ≤ 109). Gọi G là ƯCLN của cả dãy. In ra độ dài nhỏ nhất của một đoạn liên tiếp có ƯCLN bằng G.

Ví dụ:

NhậpIn ra
5
12 18 8 30 20
2

Kiểm tra: nhập

10
60 84 36 90 150 42 70 105 140 210
Xem đáp số

4

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, k (1 ≤ n ≤ 105, 0 ≤ k nhỏ hơn 230), dòng thứ hai có n số nguyên (0 ≤ ai nhỏ hơn 230). In ra số đoạn liên tiếp có AND các phần tử bằng k.

Ví dụ:

NhậpIn ra
4 2
7 6 3 2
6

Kiểm tra: nhập

10 4
12 13 4 6 15 7 5 4 12 28
Xem đáp số

40

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, q (1 ≤ n, q ≤ 105), dòng thứ hai có điểm của n vận động viên theo số áo 1, 2, …, n (0 ≤ điểm ≤ 109), q dòng sau mỗi dòng có l, r. Với mỗi câu hỏi, in ra số áo của vận động viên điểm cao nhất trong các số áo từ l đến r; nếu nhiều người bằng điểm thì in số áo lớn nhất.

Ví dụ:

NhậpIn ra
6 3
8 9 7 9 6 9
1 3
1 5
5 6
2
4
6

Kiểm tra: nhập

10 4
5 5 2 8 8 3 1 8 7 0
1 2
3 7
1 10
9 10
Xem đáp số
2
5
8
9

Hướng dẫn giải chi tiết: liên hệ tác giả.

Bảng có m hàng, n cột. Dòng đầu có m, n, q (1 ≤ m, n ≤ 300, 1 ≤ q ≤ 105), m dòng sau mỗi dòng có n số nguyên (|giá trị| ≤ 109), q dòng sau mỗi dòng có r1, c1, r2, c2 (r1 ≤ r2, c1 ≤ c2). Với mỗi câu hỏi, in ra số nhỏ nhất trong hình chữ nhật từ hàng r1 đến r2, cột c1 đến c2.

Ví dụ:

NhậpIn ra
3 4 2
5 8 2 6
7 1 9 4
3 6 5 8
1 1 2 2
2 3 3 4
1
4

Kiểm tra: nhập

4 5 3
9 4 7 3 8
2 6 1 5 9
8 3 7 2 4
5 9 6 1 3
1 1 2 3
3 4 4 5
1 1 4 5
Xem đáp số
1
1
1

Hướng dẫn giải chi tiết: liên hệ tác giả.

Có n ô đánh số 1 đến n, từ ô i luôn nhảy sang ô ti. Dòng đầu có n, q (1 ≤ n, q ≤ 105), dòng thứ hai có n số ti (1 ≤ ti ≤ n), q dòng sau mỗi dòng có x, k (1 ≤ x ≤ n, 0 ≤ k ≤ 1018). Với mỗi câu hỏi, in ra ô đang đứng sau k lần nhảy bắt đầu từ ô x.

Ví dụ:

NhậpIn ra
5 3
2 3 1 5 4
1 4
4 3
2 0
2
5
2

Kiểm tra: nhập

8 4
3 5 6 2 8 1 4 7
1 1000000000000000000
2 7
7 12345
8 1
Xem đáp số
3
8
7
7

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, S, q (1 ≤ n, q ≤ 105, 1 ≤ S ≤ 1014), dòng thứ hai có n số nguyên dương (ai ≤ S), q dòng sau mỗi dòng có l, r. Với mỗi câu hỏi, in ra số khúc ít nhất khi chia đoạn al, …, ar thành các khúc liên tiếp, mỗi khúc có tổng không quá S.

Ví dụ:

NhậpIn ra
7 10 3
4 5 3 6 2 7 1
1 7
2 4
6 6
3
2
1

Kiểm tra: nhập

10 15 4
8 7 3 9 5 6 10 2 4 12
1 10
3 6
2 9
7 8
Xem đáp số
6
2
5
1

Hướng dẫn giải chi tiết: liên hệ tác giả.