运用map优化多次查询【Kadomatsu 子序列】

问题

我们有一个长度为 N 的数组 A,要找到三元组 (i, j, k) 满足:

  1. Aᵢ : Aⱼ : Aₖ = 7 : 5 : 3

  2. j 是下标的最小值 或者​ j 是下标的最大值

数学转换

比例关系 Aᵢ : Aⱼ : Aₖ = 7 : 5 : 3 意味着存在一个正数 a,使得:

a 必须是整数,且满足:

  1. Aⱼ = 5a

  2. Aᵢ = 7a

  3. Aₖ = 3a

位置约束分析

条件 min(i, j, k) = j 或 max(i, j, k) = j 意味着:

  1. j 是最大值的情况:i 和 k 都在 j 的左侧(下标小于 j)

  2. j 是最小值的情况:i 和 k 都在 j 的右侧(下标大于 j)

注意:不可能出现 i < j < k 或 k < j < i 的情况,因为那样 j 既不是最小值也不是最大值。

算法设计

对于每个位置 j:

  1. 检查 Aj 是否能被 5 整除

  2. 如果能,计算 a = Aj/5

  3. 计算我们需要的值:

    • val_i = 7a(对应 i 位置的元素值)

    • val_k = 3a(对应 k 位置的元素值)

  4. 统计:

    • j 是最大值的情况:在 j 的左侧统计 val_i 和 val_k 的出现次数

    • j 是最小值的情况:在 j 的右侧统计 val_i 和 val_k 的出现次数

实现细节

我们需要高效地统计左右两侧某个值的出现次数。可以使用map:

  • cnt_left[x]:当前位置左侧值 x 出现的次数

  • cnt_right[x]:当前位置右侧值 x 出现的次数

处理流程:

  1. 初始化 cnt_right包含所有元素

  2. 从左到右遍历每个位置 j:

    • 将 Ajcnt_right中移除(因为它现在在"当前位置",不属于右侧)

    • 处理当前位置 j

    • 将 Aj 加入 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;

    }

相关推荐
JieE21211 小时前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE21211 小时前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术16 小时前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦17 小时前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
clint45619 小时前
C++进阶(1)——前景提要
c++
用户4978630507320 小时前
(一)小红的数组操作
算法·编程语言
夜悊1 天前
C++代码示例:进制数简单生成工具
c++
怕浪猫1 天前
Electron 系列文章封面图
算法·架构·前端框架
郝学胜_神的一滴1 天前
CMake 021: IF 条件判据详诠
c++·cmake