题解:P12085 [蓝桥杯 2023 省 B] 整数删除
_Supernova · · 题解
朴素思路
首先我们根据题意模拟出朴素思路:每次操作时,
找到最小数:遍历数组,找到最小中最靠前的数,记录
时间复杂度为
第一步优化
最外层的 偷懒方便叙述,下文复杂度皆为单次操作的时间复杂度。
对于第一步,找到最小中最可靠前的数,遍历的时间复杂度为
对于第二步,我们的操作便是弹出队首即可。时间复杂度为
对于第三步,如何快速找到左右两边未被删除的相邻数?分析问题,我们发现需要一种支持快速删除以及快速前后查找的数据结构——链表。
于是我们一开始维护一个双向链表,初始时
于是我们做到了
最后一步优化
注意到第三步操作中,我们需要把
然而,我们需要将队列中
遇到这种情况,我们先无脑将新权值加入队列,再想这个问题。
有没有什么简便的方法知道区分队列中相同
我们对每个加入的权值记录一个变量,表示这个权值的版本。越后操作修改的权值版本越新,同时我们记录全局数组
至此,本题全部结束。总时间复杂度为
以下为代码。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 500005;
int n, k;
ll a[N];
struct List {
int l, r;
} lst[N]; // 链表的维护
struct Node {
int id;
ll val;
int vrs; // 天才做法!定义数据版本!
bool operator < (const Node cmp) const {
return (val == cmp.val ? id > cmp.id : val > cmp.val);
}
};
priority_queue <Node> pq; // 优先队列,维护最小值
int version[N];
bool flag[N];
int main(void) {
cin.tie(0), cout.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
pq.push((Node){i, (ll)a[i], 0});
lst[i].l = i-1, lst[i].r = i+1; // 链表的初始化
}
lst[1].l = lst[n].r = -1;
for (int tim = 1; tim <= k; ++tim) { // 版本迭代
while (!pq.empty() && pq.top().vrs != version[pq.top().id]) pq.pop(); // 弹出旧数据
Node top = pq.top();
pq.pop();
int id = top.id;
flag[id] = true; // 标记是否被删除
ll val = top.val;
int left = lst[id].l, right = lst[id].r;
if (left != -1) { // 记得特判
lst[left].r = right;
a[left] += val;
pq.push((Node){left, a[left], tim});
version[left] = tim; // 更新版本
}
if (right != -1) { // 记得特判!
lst[right].l = left;
a[right] += val;
pq.push((Node){right, a[right], tim});
version[right] = tim; // 更新版本
}
}
for (int i = 1; i <= n; ++i) {
if (!flag[i]) {
cout << a[i] << ' ';
}
}
return 0;
}
别忘记点个赞呀。