BZOJ1150 [CTSC2007]数据备份Backup 链表+小根堆

BZOJ1150 [CTSC2007]数据备份Backup

题意:

给定一个长度为(n)的数组,要求选(k)个数且两两不相邻,问最小值是多少

题解:

做一个小根堆,把所有值放进去,当选择一个值之后,把它左右两边的值也删去,同时用两边的值减自身值放入小根堆,也就是如果不选当前值,必然要选两边的值来代替,可以自行证明
如果选的数是在两端的话,那么把相邻的值直接删掉就好了,因为如果端点值比相邻值更优,显然不可能选相邻值
用链表维护一下每个值两边的值即可

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e5+7;

int n,m,A[MAXN],l[MAXN],r[MAXN],tot;
bool used[MAXN];

int main(){
    ____();
    cin >> n >> m;
    for(int i = 0; i < n; i++) cin >> A[i];
    for(int i = n - 1; i >= 1; i--) A[i] -= A[i-1];
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > que;
    for(int i = 1; i < n; i++) que.push(make_pair(A[i],i));
    for(int i = 1; i < n; i++) l[i] = i - 1, r[i] = i + 1;
    r[tot = n-1] = 0;
    int ret = 0;
    while(m--){
        while(used[que.top().second]) que.pop();
        auto p = que.top(); que.pop();
        ret += p.first;
        if(!l[p.second]) used[r[p.second]] = true, l[r[r[p.second]]] = 0;
        else if(!r[p.second]) used[l[p.second]] = true,r[l[l[p.second]]] = 0;
        else{
            used[l[p.second]] = used[r[p.second]] = true;
            A[++tot] = A[l[p.second]] + A[r[p.second]] - A[p.second];
            l[tot] = l[l[p.second]];
            r[tot] = r[r[p.second]];
            if(l[l[p.second]]) r[l[l[p.second]]] = tot;
            if(r[r[p.second]]) l[r[r[p.second]]] = tot;
            que.push(make_pair(A[tot],tot));
        }
    }
    cout << ret << endl;
    return 0;
}