双向链表 + 优先队列
整数删除
给定一个长度为\(N\)的整数序列\(:A_1,A_2,...,A_n\)。你要重复以下操作\(K\)次:
每次选择数列中最小的整数(如果最小值不止一个,选择最靠前的),将其删除。
并把与它相邻的整数加上被删除的数值。
输出 \(K\)次操作后的序列
输入格式
第一行包含两个整数 \(N\) 和 \(K\)。
第二行包含 \(N\) 个整数\(,A1,A2,A3,…,AN\)。
输出格式
输出 \(N−K\) 个整数,中间用一个空格隔开,代表 \(K\) 次操作后的序列。
样例输入
5 3
1 4 2 8 7
样例输出
17 7
模拟一下过程:
1(最小) 4 2 8 7
-> 5 2(最小) 8 7
-> 7(最小) 10 7
-> 17 7
题解
思路:根据题意可知,\(K\)次操作,每次都要取最小值,因此这里大概可以猜一个小根堆的优先队列
又把这个最小值累加到相邻位置,这里需要一个数据结构记录相邻位置的坐标,其中双向链表可以\(O(1)\)
- 首先建立小根堆的优先队列\(q\),双向链表\(l[],r[]\),用一个数组记录元素杯累加的\(add[]\)
- 输入数据,将\([val, pos]\)存入队列,并且初始化双向链表
- 进入循环,队列最多存放\(n-k\)个元素
- 出队元素,若当前有累加值\(add\),那么把累加值加上,再入队
- 出队元素,若当前没有累加值\(add\),那么把当前的值累加到相邻点,调整链表指向
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+10;
int _;int n, k;typedef struct node {int val, pos;
}node;
int l[N], r[N];
int ans[N], add[N];struct cmp {bool operator () (const node &a, const node &b) {if(a.val == b.val) {return a.pos > b.pos;}return a.val > b.val;}
};priority_queue<node, vector<node>, cmp> q;void solve() {cin >> n >> k;for(int i = 1; i <= n; i++) {int x; cin >> x;q.push({x, i});l[i] = i - 1;r[i] = i + 1;}while(q.size() > n - k) {node t = q.top(); q.pop();if(add[t.pos]) {q.push({t.val + add[t.pos], t.pos});add[t.pos] = 0;} else {int pl = l[t.pos], pr = r[t.pos];add[pl] += t.val; add[pr] += t.val;l[pr] = pl; r[pl] = pr;}}while(q.size()) {auto [x, y] = q.top(); q.pop();ans[y] = x + add[y];}for(int i = 1; i <= n; i++) {if(ans[i]) cout << ans[i] << " ";}
}signed main() {ios::sync_with_stdio(false);cin.tie(nullptr);_ = 1;// cin >> _;while(_--) {solve();} return 0;
}