題目的難度顏色使用 Luogu 上的分級,由簡單到困難分別為 🔴🟠🟡🟢🔵🟣⚫。
Problem Statement
題目簡述
給定長度為 N N N 的整數序列 A A A ,求滿足以下條件的三元組 ( i , j , k ) (i, j, k) ( i , j , k ) 數量:
A i : A j : A k = 7 : 5 : 3 A_i : A_j : A_k = 7 : 5 : 3 A i : A j : A k = 7 : 5 : 3
j = min ( i , j , k ) j = \min(i, j, k) j = min ( i , j , k ) 或 j = max ( i , j , k ) j = \max(i, j, k) j = max ( i , j , k ) (即 j j j 是三者中最小或最大的索引)
Constraints
約束條件
1 ≤ N ≤ 3 × 1 0 5 1 \le N \le 3 \times 10^5 1 ≤ N ≤ 3 × 1 0 5
1 ≤ A i ≤ 1 0 9 1 \le A_i \le 10^9 1 ≤ A i ≤ 1 0 9
思路:前後綴分解
問題轉化
比值條件 A i : A j : A k = 7 : 5 : 3 A_i : A_j : A_k = 7 : 5 : 3 A i : A j : A k = 7 : 5 : 3 意味著存在某個正整數 v v v ,使得:
A i = 7 v A_i = 7v A i = 7 v
A j = 5 v A_j = 5v A j = 5 v
A k = 3 v A_k = 3v A k = 3 v
而位置條件「j j j 是三者中最小或最大」可分為兩種情況:
j j j 最大 :j > i j > i j > i 且 j > k j > k j > k ,即 A i A_i A i 和 A k A_k A k 都在 A j A_j A j 之前
j j j 最小 :j < i j < i j < i 且 j < k j < k j < k ,即 A i A_i A i 和 A k A_k A k 都在 A j A_j A j 之後
前綴計數
對於情況 1(j j j 最大),我們從左到右掃描陣列:
維護 pre7[v]:目前為止 A i = 7 v A_i = 7v A i = 7 v 的元素個數
維護 pre3[v]:目前為止 A k = 3 v A_k = 3v A k = 3 v 的元素個數
當遇到 A j = 5 v A_j = 5v A j = 5 v (即 A j A_j A j 是 5 的倍數)時,答案增加 pre7[v] * pre3[v]
利用對稱性
對於情況 2(j j j 最小),只需反轉陣列 後重複相同的計算即可。
反轉後,原本在 j j j 之後的元素變成在 j j j 之前,問題結構完全相同。
複雜度分析
時間複雜度:O ( N ) \mathcal{O}(N) O ( N ) ,掃描兩次陣列,每次操作為 O ( 1 ) O(1) O ( 1 ) 的 HashMap 操作
空間複雜度:O ( N ) \mathcal{O}(N) O ( N ) ,HashMap 最多儲存 N N N 個不同的 v v v 值
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from collections import defaultdictdef solve (): n = int (input ()) A = list (map (int , input ().split())) assert len (A) == n def do (A ): ans = 0 pre3 = defaultdict(int ) pre7 = defaultdict(int ) for x in A: if x % 5 == 0 : v = x // 5 ans += pre7[v] * pre3[v] if x % 3 == 0 : pre3[x // 3 ] += 1 if x % 7 == 0 : pre7[x // 7 ] += 1 return ans print (do(A) + do(A[::-1 ])) if __name__ == "__main__" : solve()