一、题目
1、题目描述
2、输入输出
2.1输入
3、原题链接
https://codeforces.com/contest/1998/problem/E1
二、解题报告
1、思路分析
对于a[i],我们记左边第一个大于a[i]的下标为pre[i],右边为suf[i]
如果sum(pre[i] + 1, suf[i] - 1) >= a[pre[i]],那么a[i] 可以和左边合并
如果sum(pre[i] + 1, suf[i] - 1) >= a[suf[i]],那么a[i] 可以和右边合并
合并意即,如果pre[i] 或者 suf[i] 中有一个可以留下,那么能够合并说明a[i] 也可以留下
那对于最大的那个元素肯定可以留下,次大的那个元素是否能留下取决于它是否可以和最大元素合并,次次大的元素取决于……
我们用单调栈预处理出pre 和 suf
然后将下标按照元素值降序排序,依次处理即可
当然这道题由于暴力合并最多发生log次,也可以采用O(Nlog^2)的暴力做法,但是单调栈的方法更加优雅
2、复杂度
时间复杂度: O(N)空间复杂度:O(N)
3、代码详解
#include <bits/stdc++.h>
#include <ranges>
// #define DEBUG
using i64 = long long;
using u32 = unsigned;
using u64 = unsigned long long;
constexpr int inf32 = 1E9 + 7;
constexpr i64 inf64 = 1E18 + 7;
constexpr double eps = 1E-9;
void solve() {
int n, x;
std::cin >> n >> x;
std::vector<int> a(n), suf(n, n), pre(n, -1), st;
std::vector<i64> acc(n + 1);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
while (st.size() && a[i] > a[st.back()]) {
suf[st.back()] = i;
st.pop_back();
}
st.push_back(i);
acc[i + 1] = acc[i] + a[i];
}
st.clear();
for (int i = n - 1; ~i; -- i) {
while (st.size() && a[i] > a[st.back()]) {
pre[st.back()] = i;
st.pop_back();
}
st.push_back(i);
}
std::vector<bool> res(n);
std::vector<int> s(n);
std::iota(s.begin(), s.end(), 0);
std::ranges::sort(s, [&](int x, int y) -> bool{
return a[x] > a[y];
});
for (int i : s) {
if (pre[i] == -1 && suf[i] == n) {
res[i] = true;
continue;
}
i64 sum = acc[suf[i]] - acc[pre[i] + 1];
if (~pre[i])
res[i] = res[i] || (sum >= a[pre[i]] && res[pre[i]]);
if (suf[i] < n)
res[i] = res[i] || (sum >= a[suf[i]] && res[suf[i]]);
}
std::cout << std::accumulate(res.begin(), res.end(), 0) << '\n';
}
auto FIO = []{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
} ();
int main() {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int t = 1;
std::cin >> t;
while (t --)
solve();
return 0;
}