题解:AtCoder AT_awc0101_e Choosing Flowerbed Intervals
【题目来源】
AtCoder:E - Choosing Flowerbed Intervals
【题目描述】
Takahashi is in charge of managing a flowerbed. The flowerbed has \(N\) flowers planted in a row, numbered \(1, 2, \ldots, N\) from left to right.
Each flower \(i\) \((1 \le i \le N)\) has a recorded "variety number" \(A_i\) and "height" \(B_i\). Takahashi wants to select a contiguous interval of flowers to create an exhibition section. An interval \([l, r]\) \((1 \le l \le r \le N)\) chosen as the exhibition section is the contiguous sequence of flowers from flower \(l\) to flower \(r\) (including the case where \(l = r\), i.e., only a single flower).
To make the exhibition section look attractive, Takahashi wants to choose an interval that satisfies both of the following 2 conditions simultaneously.
Condition 1 (Variety diversity condition):
Let \(D\) be the number of distinct values among the variety numbers \(A_l, A_{l+1}, \ldots, A_r\) of flowers in the interval \([l, r]\). The product of \(D\) and the length of the interval \((r - l + 1)\) must be at most \(K\). That is,
\(D \times (r - l + 1) \le K\)
must be satisfied.
Condition 2 (Height balance condition):
The difference between the maximum and minimum heights of flowers in the interval \([l, r]\) must be at most \(M\). That is,
\(\max_{l \le i \le r} B_i - \min_{l \le i \le r} B_i \le M\)
must be satisfied.
Find the number of intervals \([l, r]\) \((1 \le l \le r \le N)\) that satisfy both conditions above simultaneously.
高橋负责管理一个花坛。花坛里种了 \(N\) 朵花,从左到右编号为 \(1, 2, \ldots, N\)。
每朵花 \(i\) \((1 \le i \le N)\) 有一个记录的"品种编号" \(A_i\) 和"高度" \(B_i\)。高橋想选择一段连续的花来创建一个展览区。作为展览区选中的区间 \([l, r]\) \((1 \le l \le r \le N)\) 是指从第 \(l\) 朵花到第 \(r\) 朵花的连续序列(包含 \(l = r\) 的情况,即仅一朵花)。
为了让展览区看起来美观,高橋想选择一个同时满足以下两个条件的区间。
条件 1(品种多样性条件):
设 \(D\) 为区间 \([l, r]\) 中花的品种编号 \(A_l, A_{l+1}, \ldots, A_r\) 的不同值的个数。\(D\) 与区间长度 \((r - l + 1)\) 的乘积必须不超过 \(K\)。即必须满足
条件 2(高度平衡条件):
区间 \([l, r]\) 中花的最大高度与最小高度之差必须不超过 \(M\)。即必须满足
求同时满足上述两个条件的区间 \([l, r]\) \((1 \le l \le r \le N)\) 的个数。
【输入】
\(N\) \(K\) \(M\)
\(A_1\) \(A_2\) \(\ldots\) \(A_N\)
\(B_1\) \(B_2\) \(\ldots\) \(B_N\)
- The first line contains the integer \(N\) representing the number of flowers, the integer \(K\) representing the upper bound of the product in Condition 1, and the integer \(M\) representing the upper bound of the height difference in Condition 2, separated by spaces.
- The second line contains the integers \(A_1, A_2, \ldots, A_N\) representing the variety number of each flower, separated by spaces.
- The third line contains the integers \(B_1, B_2, \ldots, B_N\) representing the height of each flower, separated by spaces.
【输出】
Output in one line the number of intervals \([l, r]\) \((1 \le l \le r \le N)\) that satisfy both Condition 1 and Condition 2 simultaneously.
【输入样例】
5 6 3
1 2 1 3 2
4 5 7 6 8
【输出样例】
10
【核心思想】
-
问题分析:给定 \(N\) 朵花的品种编号 \(A_i\) 和高度 \(B_i\),需要统计满足两个约束的连续区间 \([l, r]\) 数量:条件1(品种多样性)要求不同品种数 \(D\) 与区间长度 \(\text{len}\) 的乘积 \(D \times \text{len} \leq K\);条件2(高度平衡)要求区间内最大高度与最小高度之差 \(\leq M\)。两个条件均具有单调性——当区间扩大时,\(D\)、\(\text{len}\)、高度差都可能增大,因此固定右端点时,满足条件的左端点构成一个连续后缀。
-
算法选择:
- 双指针(滑动窗口):维护一个右端点 \(j\) 递增的窗口 \([i, j]\),当窗口不满足条件时收缩左端点 \(i\)
- 单调队列:\(O(1)\) 维护窗口内的最小值和最大值,支持滑动时的快速更新
- 哈希表(
map):\(O(1)\) 维护窗口内不同品种的数量 \(D\)
-
关键步骤:
- 初始化:左指针 \(i = 1\),空单调队列
minq(递增,维护最小值)、maxq(递减,维护最大值),空频率表freq - 枚举右端点 \(j\) 从 \(1\) 到 \(N\):
- 扩展窗口:将 \(A_j\) 加入
freq,将 \(B_j\) 按规则入队(minq从队尾弹出 \(\geq B_j\) 的元素,maxq从队尾弹出 \(\leq B_j\) 的元素) - 收缩左端点:当 \(i \leq j\) 且窗口不满足条件时:
- 清理队列头部不在 \([i, j]\) 内的元素
- 计算 \(D = \text{freq.size()}\),\(\text{len} = j - i + 1\),高度差 \(= \text{maxq.front} - \text{minq.front}\)
- 若 \(D \times \text{len} \leq K\) 且高度差 \(\leq M\),停止收缩
- 否则移除 \(A_i\) 从
freq(计数归零则删除键),\(i \leftarrow i + 1\)
- 统计答案:以 \(j\) 为右端点,所有左端点 \(l \in [i, j]\) 的区间 \([l, j]\) 均满足条件,贡献 \(j - i + 1\) 个合法区间
- 扩展窗口:将 \(A_j\) 加入
- 输出累计答案 \(ans\)
- 初始化:左指针 \(i = 1\),空单调队列
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log N)\),双指针每个元素最多入队/出队各一次,单调队列操作均摊 \(O(1)\),
map操作 \(O(\log N)\) - 空间复杂度:\(O(N)\),单调队列、频率表及数组存储
- 时间复杂度:\(O(N \log N)\),双指针每个元素最多入队/出队各一次,单调队列操作均摊 \(O(1)\),
-
双指针与单调队列的核心思想:
- 单调性利用:两个条件在区间扩展时均具有"不单调递减"的特性,因此固定右端点时,满足条件的左端点集合是 \([i, j]\) 形式的一个后缀,可用双指针线性扫描
- 单调队列维护极值:通过维护队列内元素的单调性,保证队首始终为当前窗口的最值,且每个元素最多入队出队一次,实现 \(O(1)\) 查询极值
- 哈希表维护不同元素个数:
freq记录品种出现次数,键的数量即为 \(D\),支持 \(O(\log N)\) 动态更新 - 答案累加技巧:固定右端点 \(j\) 时,所有左端点 \(l \in [i, j]\) 均合法,直接累加区间长度而非逐个枚举,将统计复杂度从 \(O(N^2)\) 降至 \(O(N)\)
- 适用于带约束的连续子区间计数问题,尤其是约束具有单调性且需要动态维护统计量的场景
【算法标签】
单调队列
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 将 int 定义为 long long,防止乘积溢出
typedef pair<int, int> PII; // first: 值, second: 下标
const int N = 200005; // 最大花朵数量
int n, k, m, ans; // n: 花朵数, k: 条件1上限, m: 条件2上限, ans: 满足条件的区间数
int a[N], b[N]; // a[i]: 品种编号, b[i]: 高度
// 单调队列:维护区间最小值和最大值
deque<PII> minq, maxq; // minq: 单调递增队列(维护最小值), maxq: 单调递减队列(维护最大值)
map<int, int> freq; // freq[x]: 品种x在当前区间中的出现次数
signed main() // 使用 signed 替代 int,因为 #define int long long
{
cin >> n >> k >> m; // 读入花朵数、条件1上限、条件2上限
// 读入品种编号
for (int i = 1; i <= n; i++)
cin >> a[i];
// 读入高度
for (int i = 1; i <= n; i++)
cin >> b[i];
// 双指针:i为左端点,j为右端点
int i = 1;
for (int j = 1; j <= n; j++) // 枚举右端点j
{
// 将第j朵花加入当前区间
freq[a[j]]++; // 品种a[j]出现次数+1
// 维护单调递增队列(最小值):从队尾移除大于等于b[j]的元素
while (!minq.empty() && minq.back().first >= b[j])
minq.pop_back();
minq.push_back({b[j], j}); // 加入当前元素
// 维护单调递减队列(最大值):从队尾移除小于等于b[j]的元素
while (!maxq.empty() && maxq.back().first <= b[j])
maxq.pop_back();
maxq.push_back({b[j], j}); // 加入当前元素
// 收缩左端点i,直到区间[i,j]满足两个条件
while (i <= j)
{
// 移除队列中不在当前区间[i,j]内的元素(下标小于i)
while (!minq.empty() && minq.front().second < i)
minq.pop_front();
while (!maxq.empty() && maxq.front().second < i)
maxq.pop_front();
// 计算当前区间的条件值
int D = freq.size(); // 不同品种的数量
int len = j - i + 1; // 区间长度
// 检查两个条件是否同时满足
// 条件1:D * len <= K
// 条件2:最大值 - 最小值 <= M
if ((D * len <= k) && (maxq.front().first - minq.front().first) <= m)
break; // 满足条件,停止收缩
// 不满足条件,收缩左端点:移除第i朵花
freq[a[i]]--; // 品种a[i]出现次数-1
if (freq[a[i]] == 0)
freq.erase(a[i]); // 该品种没有了,从map中删除
i++; // 左端点右移
}
// 以j为右端点,所有左端点在[i,j]范围内的区间都满足条件
// 即区间[i,j], [i+1,j], ..., [j,j]都满足
ans += (j - i + 1); // 累加满足条件的区间数
}
// 输出满足条件的区间总数
cout << ans << endl;
return 0;
}
【运行结果】
5 6 3
1 2 1 3 2
4 5 7 6 8
10
浙公网安备 33010602011771号