基于单调栈的 O(N) 解法
输入: $N$ 头牛排成一列,每头牛高度 $h_i$ 不同。
投掷条件: 牛 $i$ 和牛 $j$ 能互相投飞盘,当且仅当:
目标: 计算所有可行对 $(i, j)$ 的距离总和。
$$ \text{Distance} = |i - j| + 1 $$
对于任意一头牛 $i$,谁是能和它扔飞盘的最远的牛?
为什么?
如果中间有比 $i$ 高的牛,挡住了视线,无法投掷。
$\Rightarrow$ 问题转化为:
寻找每头牛左右两侧“第一个比它高”的牛。
示例数据: [3, 1, 4, 2, 5]
#include <cstdio>
#include <stack>
#include <vector>
using namespace std;
typedef long long ll;
int n;
ll ans;
int a[300005];
stack<int> s; // 存储索引
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
for (int i = 0; i < n; i++) {
// 1. 右侧比栈顶高:栈顶找到右边界
while (!s.empty() && a[s.top()] < a[i]) {
ans += i - s.top() + 1;
s.pop();
}
// 2. 栈顶比当前高:当前牛找到左边界
if (!s.empty()) ans += i - s.top() + 1;
s.push(i);
}
printf("%lld\n", ans);
return 0;
}
代码中巧妙地在一个循环里处理了两种情况:
Case 1: While 循环内
a[s.top()] < a[i]
栈顶元素比较矮,它遇到了右边第一个比它高的牛 $i$。
$\rightarrow$ 计算的是以 `s.top()` 为左端点,`i` 为右端点的贡献。
Case 2: While 循环后
!s.empty() (意味着 a[s.top()] > a[i])
当前牛 $i$ 比较矮,它看到了左边第一个比它高的牛 `s.top()`。
$\rightarrow$ 计算的是以 `s.top()` 为左端点,`i` 为右端点的贡献。
while 循环处理出栈(右边界),利用剩余栈顶处理入栈(左边界)。long long。