题解:AtCoder AT_awc0026_e Just the Right Temperature Difference
【题目来源】
AtCoder:E - Just the Right Temperature Difference
【题目描述】
Takahashi works at a weather observatory and is recording the daily maximum temperatures over \(N\) days for a certain region. The maximum temperature on day \(i\) (\(1 \leq i \leq N\)) is \(A_i\) ℃.
高桥在气象观测站工作,正在记录某个地区连续 \(N\) 天的日最高气温。第 \(i\) 天(\(1 \leq i \leq N\))的最高气温为 \(A_i\) ℃。
Takahashi wants to select a contiguous interval of days and examine the temperature fluctuation range. For an interval from day \(l\) to day \(r\) (\(1 \leq l \leq r \leq N\)), the "fluctuation range" of that interval is defined as:
高桥想选择一个连续的天数区间,并检查其温度波动范围。对于从第 \(l\) 天到第 \(r\) 天(\(1 \leq l \leq r \leq N\))的区间,该区间的"波动范围"定义为:
\(\max(A_l, A_{l+1}, \dots, A_r) - \min(A_l, A_{l+1}, \dots, A_r)\)
In particular, when \(l = r\) (an interval consisting of only one day), the fluctuation range is \(0\).
特别地,当 \(l = r\)(只包含一天的区间)时,波动范围为 \(0\)。
Given a positive integer \(N\), a non-negative integer \(K\), and a sequence of maximum temperatures over \(N\) days \(A_1, A_2, \dots, A_N\), find the number of intervals whose fluctuation range is exactly \(K\). That is, find the number of integer pairs \((l, r)\) (\(1 \leq l \leq r \leq N\)) satisfying:
给定一个正整数 \(N\)、一个非负整数 \(K\),以及 \(N\) 天的最高气温序列 \(A_1, A_2, \dots, A_N\),求波动范围恰好为 \(K\) 的区间数量。也就是说,求满足以下条件的整数对 \((l, r)\)(\(1 \leq l \leq r \leq N\))的数量:
\(\max(A_l, A_{l+1}, \dots, A_r) - \min(A_l, A_{l+1}, \dots, A_r) = K\)
【输入】
\(N\) \(K\)
\(A_1\) \(A_2\) \(\dots\) \(A_N\)
The first line contains a positive integer \(N\) representing the number of days and a non-negative integer \(K\) representing the specified fluctuation range, separated by a space. The second line contains integers \(A_1, A_2, \dots, A_N\) representing the maximum temperature on each day, separated by spaces.
【输出】
Output the number of pairs \((l, r)\) whose fluctuation range is exactly \(K\), on a single line.
【输入样例】
5 3
1 4 2 5 3
【输出样例】
6
【核心思想】
-
问题分析:给定长度为 \(N\) 的数组 \(A_1, A_2, \dots, A_N\),需要统计满足 \(\max(A_l, \dots, A_r) - \min(A_l, \dots, A_r) = K\) 的区间 \([l, r]\) 数量。这是一个双指针 + 单调队列问题,关键在于高效维护区间内的最大值和最小值。
-
算法选择:
- 双指针(Two Pointers):维护一个滑动窗口 \([l, r]\),通过移动左右指针来枚举所有可能的区间
- 单调队列(Monotonic Deque):分别用两个单调队列维护当前窗口内的最小值和最大值,实现 \(O(1)\) 查询区间极值
- 容斥原理:差值恰好等于 \(K\) 的区间数 = 差值不超过 \(K\) 的区间数 - 差值不超过 \(K-1\) 的区间数
-
关键步骤:
- 初始化:读取 \(N\)(天数)、\(K\)(目标波动范围)、\(A[1..N]\)(温度序列)
- calc(k)函数:计算差值不超过 \(k\) 的区间数量
- 使用双指针 \(l\) 和 \(r\) 维护当前区间
dqmin:单调递增队列,维护区间最小值(队首为最小值)dqmax:单调递减队列,维护区间最大值(队首为最大值)
- 扩展右指针:
- 将 \(A[r]\) 加入
dqmin:从队尾弹出所有 \(\geq A[r]\) 的元素,然后入队 - 将 \(A[r]\) 加入
dqmax:从队尾弹出所有 \(\leq A[r]\) 的元素,然后入队
- 将 \(A[r]\) 加入
- 收缩左指针:
- 当
dqmax.front().v - dqmin.front().v > k时,说明当前区间不满足条件 - 移动左指针 \(l\):如果队首元素的下标等于 \(l\),则从队列中弹出
- 重复直到区间满足条件
- 当
- 统计区间数:以 \(r-1\) 为右端点的满足条件的区间数量为 \(r - l + 1\),累加到答案
- 容斥计算:
ans = calc(K) - calc(K-1)
-
时间/空间复杂度:
- 时间复杂度:\(O(N)\),每个元素最多入队和出队各一次
- 空间复杂度:\(O(N)\),两个单调队列的空间
-
双指针与单调队列的核心思想:
- 滑动窗口:双指针维护一个动态区间,右指针扩展,左指针收缩,保证窗口内始终满足条件
- 单调队列维护极值:通过维护单调性,使得队首始终是当前窗口的最值,实现 \(O(1)\) 查询
- 过期元素处理:当左指针移动时,检查队首元素是否已过期(下标 < l),及时弹出
- 容斥转化:将"恰好等于 \(K\)"转化为"不超过 \(K\)"的差值,简化问题处理
- 适用于区间统计、滑动窗口最值类问题
【解题思路】

【算法标签】
双指针
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005;
int n, k;
int a[N], ans;
struct Node
{
int v, idx; // v: 数组值, idx: 在数组中的下标
};
// 计算最大值与最小值之差不超过k的区间数量
int calc(int k)
{
int res = 0; // 结果计数器
int l = 1, r = 1; // 双指针,表示当前区间[l, r]
deque<Node> dqmin, dqmax; // 单调队列,分别维护最小值和最大值
while (l <= n && r <= n) // 双指针遍历数组
{
// 将a[r]加入最小值队列(维护单调递增)
while (!dqmin.empty() && dqmin.back().v >= a[r])
{
dqmin.pop_back(); // 移除队尾比a[r]大的元素
}
dqmin.push_back({a[r], r}); // 将a[r]加入队列
// 将a[r]加入最大值队列(维护单调递减)
while (!dqmax.empty() && dqmax.back().v <= a[r])
{
dqmax.pop_back(); // 移除队尾比a[r]小的元素
}
dqmax.push_back({a[r], r}); // 将a[r]加入队列
// 调整左指针l,使当前区间[l, r]的最大值-最小值<=k
while (!dqmin.empty() && !dqmax.empty() && dqmax.front().v - dqmin.front().v > k)
{
// 如果队首元素是左指针l指向的元素,则从队列中移除
if (dqmax.front().idx == l)
{
dqmax.pop_front(); // 从最大值队列中移除过期的元素
}
if (dqmin.front().idx == l)
{
dqmin.pop_front(); // 从最小值队列中移除过期的元素
}
l++; // 左指针右移,缩小区间
}
r++; // 右指针右移,扩展区间
// 统计以r-1为右端点的满足条件的区间数量
// 区间[l, r-1]满足条件,所以有(r-1)-l+1 = r-l+1个区间
res += r - l + 1;
}
return res; // 返回差值不超过k的区间总数
}
signed main()
{
cin >> n >> k; // 读入数组长度n和差值限制k
for (int i = 1; i <= n; i++)
{
cin >> a[i]; // 读入数组元素
}
// 差值恰好等于k的区间数 = 差值不超过k的区间数 - 差值不超过k-1的区间数
cout << calc(k) - calc(k - 1) << endl;
return 0;
}
【运行结果】
5 3
1 4 2 5 3
6
浙公网安备 33010602011771号