问题
我们有一个长度为 N 的数组 A,要找到三元组 (i, j, k) 满足:
-
Aᵢ : Aⱼ : Aₖ = 7 : 5 : 3
-
j 是下标的最小值 或者 j 是下标的最大值
数学转换
比例关系 Aᵢ : Aⱼ : Aₖ = 7 : 5 : 3 意味着存在一个正数 a,使得:
a 必须是整数,且满足:
-
Aⱼ = 5a
-
Aᵢ = 7a
-
Aₖ = 3a
位置约束分析
条件 min(i, j, k) = j 或 max(i, j, k) = j 意味着:
-
j 是最大值的情况:i 和 k 都在 j 的左侧(下标小于 j)
-
j 是最小值的情况:i 和 k 都在 j 的右侧(下标大于 j)
注意:不可能出现 i < j < k 或 k < j < i 的情况,因为那样 j 既不是最小值也不是最大值。
算法设计
对于每个位置 j:
-
检查 A[j] 是否能被 5 整除
-
如果能,计算 a = A[j]/5
-
计算我们需要的值:
-
val_i = 7a(对应 i 位置的元素值)
-
val_k = 3a(对应 k 位置的元素值)
-
-
统计:
-
j 是最大值的情况:在 j 的左侧统计 val_i 和 val_k 的出现次数
-
j 是最小值的情况:在 j 的右侧统计 val_i 和 val_k 的出现次数
-
实现细节
我们需要高效地统计左右两侧某个值的出现次数。可以使用map:
-
cnt_left[x]:当前位置左侧值 x 出现的次数 -
cnt_right[x]:当前位置右侧值 x 出现的次数
处理流程:
-
初始化
cnt_right包含所有元素 -
从左到右遍历每个位置 j:
-
将 A[j] 从
cnt_right中移除(因为它现在在"当前位置",不属于右侧) -
处理当前位置 j
-
将 A[j] 加入
cnt_left(为下一个位置准备)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);int N; cin >> N; vector<int> A(N); for (int i = 0; i < N; i++) { cin >> A[i]; } // cnt_left: j左侧的值计数,cnt_right: j右侧的值计数 unordered_map<ll, int> cnt_left, cnt_right; // 初始化cnt_right包含所有元素 for (int i = 0; i < N; i++) { cnt_right[A[i]]++; } ll ans = 0; // 遍历每个位置作为j for (int j = 0; j < N; j++) { // 将A[j]从右侧计数中移除(现在它位于"当前位置",不属于右侧) cnt_right[A[j]]--; // 检查A[j]是否能被5整除 if (A[j] % 5 == 0) { ll a = A[j] / 5; // 计算比例因子 // 计算需要的i和k对应的值 ll val_i = 7 * a; // A[i] 应该是 7a ll val_k = 3 * a; // A[k] 应该是 3a // 情况1: j是最大值(i和k在j左侧) // cnt_left[val_i] 是左侧val_i的个数 // cnt_left[val_k] 是左侧val_k的个数 // 每个val_i和每个val_k可以组成一个三元组 ans += (ll)cnt_left[val_i] * cnt_left[val_k]; // 情况2: j是最小值(i和k在j右侧) ans += (ll)cnt_right[val_i] * cnt_right[val_k]; } // 将A[j]加入左侧计数(为下一个j准备) cnt_left[A[j]]++; } cout << ans << "\n"; return 0;}
-