题解:CF2205D
Wyh_dailyAC · · 题解
D - Simons and Beating Peaks
观点
题出的好!覆盖知识点广,题目有着切合实际的背景,解法比较自然。给出题人点赞!更重要的是数据基本正确,符合一道好题的基本标准!
题意
定义一个长
给出一个长
每次操作你可以令
Sol
首先,你可以敏锐地发现,如果我们以下标为
下标顺序 & 区间最大,很容易想到笛卡尔树啊。
对原排列建
把“当前还没被删掉”的元素看成原数组的一个子序列,保持相对顺序不变。
设最终留下的集合为
主题:最终留下的点必须是一条根到叶路径。
断言
证明: 因为中序顺序,
即
由断言
因为全局最大值(根)永远不能被删除操作(即只能删峰的邻居,不删峰本身),且最终数组必须连通成一个序列,所以
于是可行保留长度
主题:任意根到叶路径都能通过合法操作留下。
取任意一条根到叶路径
从根开始沿
- 若
v 在左子树,则要删空右子树; - 若
v 在右子树,则要删空左子树; - 若
u 是路径末端叶,则删空它的两侧(除路径外的部分)。
关键点:当非路径侧还存在元素、且路径侧也存在元素时,
每次删除都会减少非路径侧剩余元素数,故该过程必在有限步内删空非路径侧。对路径上节点自顶向下重复,最终所有不在
因此最长可保留长度
结合上面内容,我们便有了结论:
最少操作次数
其中
于是这题做完了。注意赛时可以去猜结论。
Code
代码非常短。
#include <bits/stdc++.h>
using namespace std;
auto main() -> signed {
int T; cin >> T;
while (T--) {
int n; cin >> n;
vector<int> a(n + 5);
for (int i = 1; i <= n; ++i) cin >> a[i];
vector<int> l(n + 5), r(n + 5), p(n + 5);
vector<int> st;
for (int i = 1; i <= n; ++i) {
int last = 0;
while (!st.empty() && a[st.back()] < a[i]) {
last = st.back();
st.pop_back();
}
if (!st.empty()) {
r[st.back()] = i;
p[i] = st.back();
}
if (last) {
l[i] = last;
p[last] = i;
}
st.emplace_back(i);
}
int rt = st.front();
while (p[rt]) rt = p[rt];
vector<int> dep(n + 5);
queue<int> q;
dep[rt] = 1;
q.emplace(rt);
int h = 1;
while (!q.empty()) {
int u = q.front();
q.pop();
h = max(h, dep[u]);
if (l[u]) {
dep[l[u]] = dep[u] + 1;
q.emplace(l[u]);
}
if (r[u]) {
dep[r[u]] = dep[u] + 1;
q.emplace(r[u]);
}
}
cout << (n - h) << "\n";
}
}