一、题目
1、题目描述
2、输入输出
2.1输入
2.2输出
3、原题链接
E2 - Eliminating Balls With Merging (Hard Version)
二、解题报告
1、思路分析
前缀和+单调栈,CF1998 E1 - Eliminating Balls With Merging (Easy Version)-CSDN博客
和E1同样是单调栈预处理pre[i],suf[i],代表每个下标左边右边第一个大于自己的
然后还是将下标索引按照值排序
然后从大到小处理每个下标
当处理下标i时,如果pre[i] 和 suf[i]存在,那么他们因为值大,已经被提前处理了
我们只需判断 i 是否能与其合并,所以我们开两个数组start[i] end[i] 记录每个 i 可以被保留的区间(左闭右开)
我们二分查找b,b 是满足 acc[b] - acc[pre[i] + 1] >= a[pre[i]] 的最小下标(b >= i + 1)
下面分情况讨论:
如果 b <= suf[i]:
如果pre[i] 存在,那么我们左合并,start[i] = max(start[pre[i]], b - 1),end[i] = end[pre[i]]
如果pre[i] 不存在,说明 a[i] 是前缀最值,我们只需看是否能右合并
start[i] = b - 1,end[i] = s < a[suf[i]] ? suf[i] : end[suf[i]]
否则,如果b > suf[i]
如果suf[i]不存在,或者 不能右合并
那么 start[i] = i, end[i] = suf[i]
否则,我们右合并,start[i] = start[suf[i]],end[i] = end[suf[i]]
然后计算出区间的时候对差分数组修改,最后遍历差分数组求和就行了
对于有些DSU做法真的看不懂……
2、复杂度
时间复杂度: O(NlogN)空间复杂度: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), st, pre(n, -1), suf(n, n), start(n), end(n);
std::vector<i64> acc(n + 1);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
acc[i + 1] = acc[i] + a[i];
while (st.size() && a[i] > a[st.back()]) {
suf[st.back()] = i;
st.pop_back();
}
st.push_back(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<int> id(n);
std::iota(id.begin(), id.end(), 0);
std::ranges::sort(id, [&](int i, int j) -> bool {
return a[i] > a[j];
});
std::vector<int> dif(n + 1);
auto get = [&](int i, int lo, int hi) -> int {
while (lo < hi) {
int x = lo + hi >> 1;
if (acc[x] - acc[pre[i] + 1] - (~pre[i] ? a[pre[i]] : 0) >= 0)
hi = x;
else
lo = x + 1;
}
return lo;
};
for (int i : id) {
if (pre[i] == -1 && suf[i] == n) {
++ dif[start[i] = i];
-- dif[end[i] = n];
continue;
}
i64 s = acc[suf[i]] - acc[pre[i] + 1];
auto b = get(i, i + 1, suf[i] + 1); // 前缀和下标为b,实际为b - 1
if (b <= suf[i]) {
if (pre[i] >= 0) {
++ dif[start[i] = std::max(start[pre[i]], b - 1)];
-- dif[end[i] = end[pre[i]]];
} else {
++ dif[start[i] = b - 1];
-- dif[end[i] = s < a[suf[i]] ? suf[i] : end[suf[i]]];
}
} else if (suf[i] == n || (suf[i] < n && s < a[suf[i]])) {
++ dif[start[i] = b - 1];
-- dif[end[i] = suf[i]];
} else {
++ dif[start[i] = start[suf[i]]];
-- dif[end[i] = end[suf[i]]];
}
}
for (int i = 0, s = 0; i < n; ++ i) {
s += dif[i];
std::cout << s << " \n"[i + 1 == 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;
}