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