双向链表 + 优先队列

双向链表 + 优先队列

整数删除

给定一个长度为\(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)\)

  1. 首先建立小根堆的优先队列\(q\),双向链表\(l[],r[]\),用一个数组记录元素杯累加的\(add[]\)
  2. 输入数据,将\([val, pos]\)存入队列,并且初始化双向链表
  3. 进入循环,队列最多存放\(n-k\)个元素
    1. 出队元素,若当前有累加值\(add\),那么把累加值加上,再入队
    2. 出队元素,若当前没有累加值\(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;
}
posted @ 2025-03-21 00:58  Evan619  阅读(18)  评论(0)    收藏  举报