bzoj1150: [CTSC2007]数据备份Backup

题目大意: 在n个点中,选出k对相邻的互不相同的点,使k段距离的总和最小。

贪心,双向链表。

首先,点之间的距离是动态的,所以要用堆来维护。

 

每次都选择最近的点。但因为其他情况,可能最终不会选择这对点连在一起

所以把俩个点旁边的路径的和减去俩个点之间距离再加进去,表示连旁边的俩条边,不连现在的边。

要维护许多信息。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 400000 + 10;
const int INF = 0x3f3f3f3f;
struct heap {
    int v,i;
} h[maxn];
int p[maxn],pre[maxn],next[maxn],pos[maxn];
int n,m,ans=0,size=0;

void pushup(int x) {
    while(h[x].v<h[x>>1].v) {
        pos[h[x>>1].i]=x;
        swap(h[x],h[x>>1]);
        x>>=1;
    }
    pos[h[x].i]=x;
}

void push(int v,int i) {
    h[++size].v=v;
    h[size].i=i;
    pos[i]=size;
    pushup(size);
}

void pushdown(int x) {
    int to;
    while(x <= size/2) {
        to=x<<1;
        if(to<size && h[to].v>h[to+1].v) to++;
        if(h[x].v > h[to].v) {
            pos[h[to].i]=x;
            swap(h[x],h[to]);    
            x=to;
        }
        else break;
    }
    pos[h[x].i]=x;
}

void del(int x) {
    h[x].v = INF;
    pushdown(x);
}

void init() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&p[i]);
    for(int i=2;i<=n;i++) {
        push(p[i]-p[i-1],i);
        pre[i]=i-1; next[i]=i+1;
    }
    pre[2]=next[n]=0;
}

void solve() {
    int a,b;
    heap k;
    while(m--) {
        k=h[1];
        if(pre[k.i]==-1) {
            ans+=k.v;
            del(1); del(pos[next[k.i]]);
            pre[next[next[k.i]]]=-1;
        }
        else if(next[k.i]==-1) {
            ans+=k.v;
            del(1); del(pos[pre[k.i]]);
            next[pre[pre[k.i]]]=-1;
        }
        else {
            ans+=k.v;
            a=next[k.i]; b=pre[k.i];
            pre[k.i]=pre[b];  next[pre[k.i]]=k.i;
            next[k.i]=next[a];  pre[next[k.i]]=k.i;
            h[1].v=h[pos[a]].v+h[pos[b]].v-h[1].v;
            del(pos[a]); del(pos[b]);
            pushdown(1);
        }
    }
    printf("%d\n",ans);
}

int main() {
    init();
    solve();
    return 0;    
}
posted @ 2016-05-27 20:39  invoid  阅读(557)  评论(0编辑  收藏  举报