Phần giải thích code, tại nó dài quá, nên viết nháp rồi sửa đi sửa lại. Cuối cùng đăng trễ. :v
Đổi cái mảng số nguyên thành mảng các pair. Giá trị thứ nhất là số nguyên key
, giá trị thứ 2 là số lần lặp key
trong mảng ban đầu. Ví dụ:
1 1 1 4 4 2 3 3 3 3
(1,3) (4,2) (2,1) (3,4)
2 2 2 2 2
(2,5)
4 3 5 1
(4,1) (3,1) (5,1) (1,1)
Thay vì sort mảng số nguyên, thì sort mảng các pair rồi chuyển sang lại mảng số nguyên.
(1,3) (2,1) (3,4) (4,2)
1 1 1 2 3 3 3 3 4 4
(2,5)
2 2 2 2 2
(1,1) (3,1) (4,1) (5,1)
1 3 4 5
Xét một mảng các pair có dạng: (b, l) (a1, l1) (a2, l2) … (an, ln), trong đó:
- a1 < a2 < … < an < b,
- ai, b, n là số nguyên dương.
Sau khi sort dãy trên, có được mảng: (a1, l1) (a2, l2) … (an, ln) (b, l).
Gọi tổng số lần chuyển ai lên đầu mảng trong khi sort là T(n).
Trường hợp n = 1: (b, l) (a1, l1)
- (a1, 1) (b, l) (a1, l1 - 1), vì b > a1, có 1 bước
- (a1, 2) (b, l) (a1, l1 - 2), có 1 bước
- …
- (a1, l1 - 1) (b, l) (a1, 1), có 1 bước
- (a1, l1) (b, l), có 1 bước
Tổng cộng T(1) = l1
Trường hợp n = 2: (b, l) (a1, l1) (a2, l2)
- (a1, l1) (b, l) (a2, l2), vì b > a1, có l1 bước
- (a2, 1) (a1, l1) (b, l) (a2, l2 - 1), vì b > a2, có 1 bước
- (a1, l1) (a2, 1) (b, l) (a2, l2 - 1), vì a2 > a1, có l1 bước
- (a2, 1) (a1, l1) (a2, 1) (b, l) (a2, l2 - 2), vì b > a2, có 1 bước
- (a1, l1) (a2, 2) (b, l) (a2, l2 - 2), vì a2 > a1, có l1 bước
- …
- (a2, 1) (a1, l1) (a2, l2 - 1) (b l), có 1 bước
- (a1, l1) (a2, l2) (b l), có l1 bước
Tổng cộng T(2) = l1 + l2 . (l1 + 1)
Trường hợp n > 2: (b, l) (a1, l1) (a2, l2) … (an, ln)
- (a1, l1) (b, l) (a2, l2) … (an, ln), vì b > a1, có l1 bước
- (a1, l1) … (ai-1, li-1) (b, l) (ai, li) … (an, ln), với 2 ≤ i ≤ n
- (ai, 1) (a1, l1) … (ai-1, li-1) (b, l) (ai, li - 1) … (an, ln), vì b > ai, có 1 bước
- (a1, l1) … (ai-1, li-1) (ai, 1) (b, l) (ai, li - 1) … (an, ln)
- xét mảng (ai, 1) (a1, l1) … (ai-1, li-1), lấy b = ai, và n = i-1, được T(i-1) bước
- (ai, 1) (a1, l1) … (ai-1, li-1) (ai, 1) (b, l) (ai, li - 2) … (an, ln), vì b > ai, có 1 bước
- (a1, l1) … (ai-1, li-1) (ai, 2) (b, l) (ai, li - 2) … (an, ln), với b = ai và n = i-1, được T(i-1) bước
- …
- (ai, 1) (a1, l1) … (ai-1, li-1) (ai, li - 1) (b, l) … (an, ln), có 1 bước
- (a1, l1) … (ai-1, li-1) (ai, li) (b, l) … (an, ln), có T(i-1) bước
- tổng cộng = li . (T(i-1) + 1)
Tổng cộng
&space;=&space;l_1&space;+&space;%5Csum_%7Bi=2%7D%5E%7Bn%7Dl_i(T(i-1)+1))
+1)&space;+&space;l_n(T(n-1)+1)&space;=&space;T(n-1)&space;+&space;l_n(T(n-1)+1))
T(n-1)&space;+&space;l_n)
Từ hệ thức truy hồi T(n), chứng minh công thức tổng quát của T(n) bằng phương pháp quy nạp:
&space;=&space;%5Cprod_%7Bi=1%7D%5E%7Bn%7D%5Cleft(&space;l_i+1&space;%5Cright)&space;-&space;1)
Bước cơ sở:
&space;=&space;%5Cprod_%7Bi=1%7D%5E%7B1%7D(l_i+1)-1&space;=&space;l_1)
&space;=&space;%5Cprod_%7Bi=1%7D%5E%7B2%7D(l_i+1)-1&space;=&space;(l_1+1)(l_2+1)&space;-&space;1&space;=&space;l_1&space;+&space;l_2(l_1+1))
Bước quy nạp (n ≥ 2):
&space;=&space;(l_%7Bn+1%7D+1)T(n)&space;+&space;l_%7Bn+1%7D)
![= (l_{n+1}+1)\left[\prod_{i=1}^{n}(l_i+1) - 1\right ] + l_{n+1} = (l_{n+1}+1)\prod_{i=1}^{n}(l_i+1) - (l_{n+1}+1) + l_{n+1}](https://latex.codecogs.com/png.latex?%5Cdpi%7B120%7D&space;%5Cbg_white&space;=&space;(l_%7Bn+1%7D+1)%5Cleft%5B%5Cprod_%7Bi=1%7D%5E%7Bn%7D(l_i+1)&space;-&space;1%5Cright&space;%5D&space;+&space;l_%7Bn+1%7D&space;=&space;(l_%7Bn+1%7D+1)%5Cprod_%7Bi=1%7D%5E%7Bn%7D(l_i+1)&space;-&space;(l_%7Bn+1%7D+1)&space;+&space;l_%7Bn+1%7D)
&space;-&space;1)
Xét một dạng mảng khác tổng quát hơn: (a1, l1) (a2, l2) … (ak, lk) (ak+1, lk+1) … (an, ln) (b, l)
- a1 < a2 < … < ak < b < ak+1 < … < an,
- ai, b, n là các số nguyên dương.
Sau khi sort, ta được mảng: (a1, l1) (a2, l2) … (ak, lk) (b, l) (ak+1, lk+1) … (an, ln)
Gồm có 2 giai đoạn để sắp xếp mảng trên.
Giai đoạn 1: vì an > b nên (b, l) được chuyển đầu mảng, có l bước, thu được mảng
- (b, l) (a1, l1) (a2, l2) … (ak, lk) (ak+1, lk+1)
Giai đoạn 2: chèn (b, l) vào giữa (ak, lk) và (ak+1, lk+1), số bước bằng T(k) (sắp xếp (b, l) vào mảng (a11, l1) … (ak, lk)).
- (a11, l1) (a2, l2) … (ak, lk) (b, l) (ak+1, lk+1) … (an, ln)
Tổng cộng: l + T(k)
Từ các tính toán trên, xây được giải thuật, trong đó inputs
và arr
là mảng các (ai, li), inputs
là mảng ban đầu, arr
là mảng các phần tử đã được duyệt bởi vòng lặp for
:
Nếu sử dụng giải thuật trên, for
chạy trong O(n), lệnh gán count
chạy trong O(n). Do đó, tổng cộng thời gian chạy là O(n2), không thoả mãn yêu cầu đề bài. Nên cần phải có cách để giảm lệnh cập nhật count
từ O(n) xuống còn O(logn). Một cách giải quyết là sử dụng self-balancing tree.
Tree được xây dựng dựa trên ý tưởng của AVL Tree, nhưng thay vì các giá trị value
được lưu trữ ở tất cả các node, thì tree có 2 loại node.
- Leaf node: node lá lưu trữ giá trị chính,
key
là b và value
là l+1. Nếu duyệt theo inorder traversing thì thứ tự các node lá in ra theo thứ tự tăng dần.
- Node còn lại: node lưu trữ giá trị được tính từ các subtree (computed value), đại diện cho một tập các node lá.
- Giá trị
[start, end]
bao phủ tất cả giá trị key
của các node lá, start
cho giá trị key
nhỏ nhất mà node bao phủ, end
cho giá trị key
lớn nhất mà node bao phủ.
- Giá trị
value
là tích các của các value
của node lá.
Ví dụ:

- Node lá lần lượt lưu theo thứ tự từ trái sang phải: (0,3), (1,5), (2,4), (3,4), (5,3)
- Node [0,1] bao phủ (0,3), (1,5), có
value
= 15 = 3.5
- Node [0,2] bao phủ (0,3), (1,5), (2,4), có
value
= 60 = 15.4 = 3.5.4
- Node [3,5] bao phủ (3,4), (5,3), có
value
= 12 = 4.3
- Node [0,5] bao phủ (0,3), (1,5), (2,4), (3,4), (5,3), có
value
= 720 = 60.12 = (15.4) . (4.3) = 3.5.4.4.3
3 thao tác chính trên tree, mỗi thao tác đều là O(logn)
-
max
: duyệt từ root sang nhánh phải liên tục đến khi gặp leaf node, trả về value
của leaf node.
-
insert
: cập nhật hoặc thêm node mới vào tree.
- nếu đã tồn tại
key
trong tree, cập nhật lại value
của leaf node chứa key
, cập nhật giá trị value
của các node cha.
- nếu chưa tồn tại
key
, tạo node mới tạo vị trí leaf node có key
gần bằng với key
, cập nhật các node cha, tự động chỉnh tree cân bằng nếu tạo node làm mất cân bằng tree.
-
count
: Trả về giá trị tích (li + 1)-1, có ai nhỏ hơn key
= b. Ví dụ count(4)
count(4) = count([0,2]) * count[3,4]) - 1
count([0,2]) = 60
count([3,4]) = count((3,4)) = 4
count(4) = 60 * 4 - 1 = 240 - 1 = 239