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

    }

相关推荐
冻柠檬飞冰走茶8 小时前
PTA基础编程题目集 7-20 打印九九口诀表(C语言实现)
c语言·开发语言·数据结构·算法
鱼子星_8 小时前
《算 · 法》题目解析篇(1):最近公共祖先问题,线段树,最长回文子序列
c++·算法·动态规划·递归
雾喔8 小时前
算法练习5
算法·代理模式
问商十三载8 小时前
RAG 检索召回越多越好?2026 信噪比模型深度解析
人工智能·算法·机器学习
额恩668 小时前
ResearchPilot 第三阶段总结报告
python·算法
Hi李耶8 小时前
【LeetCode】9-回文数
算法·leetcode·职场和发展
红叶舞8 小时前
基于线段树的数据结构
数据结构·python·算法
chnyi6_ya8 小时前
论文阅读笔记 | The Script is All You Need: 对话到电影级视频生成的 Agentic 框架
论文阅读·笔记
听雨入夜9 小时前
CMake构建学习笔记-libxml库的构建
笔记·学习
iiiiii119 小时前
【论文阅读笔记】Reparameterization Proximal Policy Optimization (RPO):将PPO的稳定性引入可微分强化学习
论文阅读·人工智能·笔记·深度学习·机器学习·机器人·具身智能