运用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. 检查 A[j] 是否能被 5 整除

  2. 如果能,计算 a = A[j]/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:

    • 将 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;

    }

相关推荐
TTTrees2 小时前
C++学习笔记(31):智能指针(shared_ptr)
c++
苦瓜小生2 小时前
【黑马点评学习笔记 | 实战篇 】| 10-用户签到+UV统计
笔记·后端·学习
_饭团2 小时前
C 语言内存函数全解析:从 memcpy 到 memcmp 的使用与模拟实现
c语言·开发语言·c++·学习·算法·面试·改行学it
24白菜头2 小时前
第十五届蓝桥杯C&C++大学B组
数据结构·c++·笔记·学习·算法·leetcode·蓝桥杯
学嵌入式的小杨同学2 小时前
STM32 进阶封神之路(二十二):DMA 实战全攻略 ——ADC 采集 + 串口收发 + 内存复制(库函数 + 代码落地)
c++·stm32·单片机·嵌入式硬件·mcu·硬件架构·pcb
2401_884563242 小时前
C++中的观察者模式实战
开发语言·c++·算法
qcwl662 小时前
深入理解Linux进程与内存 学习笔记#3
linux·笔记·学习
-Springer-2 小时前
STM32 学习 —— 个人学习笔记10-1(I2C 通信协议及 MPU6050 简介 & 软件 I2C 读写 MPU6050)
笔记·stm32·学习
凌波粒2 小时前
LeetCode--704.二分查找(数组)
算法·leetcode·职场和发展