运用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:

    • 将 Aj 从 cnt_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;

    }

相关推荐
小郎君6584 小时前
MPTT 不是万能编号:一棵百万节点树的增删改与批量事务
算法
FPGA小徐4 小时前
【一生一芯 / PA】AM 核心文件逐行解析(上):从 start.S 到 gpu.c
前端·算法
深圳老胡4 小时前
STM32CubeMX 2 与 STM32CubeMX 的主要区别
笔记·stm32·单片机·代码规范
Tairitsu_H4 小时前
[动态规划#1] 子数组 | 最大子数组和 | 环形子数组的最大和 | 乘积最大子数组 | 乘积为正数的最长子数组
算法·动态规划
叼烟扛炮5 小时前
Linux 学习笔记(十三):高效排错利器——GDB 调试器入门
linux·笔记·学习
凯瑟琳.奥古斯特5 小时前
动态调优IoU提升OCR文本合并精度
开发语言·python·算法
布莱克6055 小时前
A* 算法详解:从原理到实现
开发语言·c++·算法
多弗朗皮卡丘5 小时前
C++ 智能指针
开发语言·c++·c++11·智能指针
段一凡-华北理工大学5 小时前
高炉炼铁机器视觉与智能识别十八讲~系列文章15:可靠性篇:防误报、容错与识别系统的鲁棒性设计
人工智能·算法·机器学习·高炉大模型·高炉机器视觉·高炉炼铁模型开发
进化中的刀盾兽6 小时前
从零实现 C++ AI 大模型接入 SDK(十):SessionManager 会话管理与多轮对话实现
c++