[codevs1615]数据备份

来源:互联网 发布:刀具查询软件 编辑:程序博客网 时间:2024/05/29 02:49

题目←

大意:
可以转换成n条线段中选k条端点不重合的线段,要求线段之和最小
发现一条线段选了之后会影响两边的,考虑链表
还有这样一条替代关系:

选择一条长度为v的线段后,两旁的线段不能再选,但若要重选两条线段,可将答案增加lv+rv-v来抵消
其中lv、rv为左右线段的价值

综上,选择一条价值为v的线段时,在链表空间中新加入lv+rv-v,代替v的位置
然后继续从小到大贪心就可以了

#include<iostream>#include<cstdio>#include<algorithm>#include<queue>#define LL long long#define INF 1061109567using namespace std;const int MAXN = 1000000 + 50;LL next[MAXN],pre[MAXN],A[MAXN],D[MAXN];struct zt{    LL pos;    LL cost,v;}l[MAXN];bool operator < (zt a,zt b){    return a.v > b.v;}priority_queue <zt> q;LL n,m,k,cnt;int first;LL tot,ans,now;bool del[MAXN];int main(){    scanf("%lld%lld",&n,&m);    scanf("%lld",&D[1]);    for(int i = 2;i <= n;i ++){        scanf("%lld",&D[i]);        A[++ cnt] = D[i] - D[i - 1];    }    for(int i = 1;i <= cnt;i ++){        pre[i] = i - 1;        next[i - 1] = i;        q.push((zt){i,1,A[i]});    }    pre[0] = -1;    next[cnt] = cnt + 1;    first = 0;    A[0] = INF;    A[cnt + 1] = INF;    cnt ++;    while(!q.empty())    {        zt u = q.top();        q.pop();        if(del[u.pos])continue;        tot += u.cost;        now += u.v;        if(tot == m){            ans = now;            break;        }        u.v = -u.v;        u.v += A[pre[u.pos]] + A[next[u.pos]];        A[++ cnt] = u.v;        int tmp1 = pre[u.pos],tmp2 = next[u.pos];        next[pre[tmp1]] = cnt;        pre[next[tmp2]] = cnt;        pre[cnt] = pre[tmp1];        next[cnt] = next[tmp2];        del[tmp1] = del[tmp2] = del[u.pos] = true;        u.pos = cnt;        q.push(u);    }    printf("%lld",ans);}