[ CTSC 2007 / BZOJ 2151 ] Backup / 种树

\(\\\)

\(Description\)


给出一个数轴上\(N\)个点的坐标\(A_i\),选择\(K\)个点对,使得这\(K\)个点对每个点对的距离之和尽可能小。

  • \(N\in [0,10^5]\)\(K\in [0,\frac{N}{2}]\)\(A_i\in [0,10^9]\)

\(\\\)

\(Solution\)


  • 排序,求出相邻两两间距\(D_i\),问题转化为选\(K\)互不相邻的间距
  • 将所有的间距放到一个小根堆中,每次取堆顶加入答案。如果选择了\(D_i\),则在堆中删去\(D_i,D_{i-1},D_{i+1}\),并将\(D_{i-1}+D_{i+1}-D_i\)加入堆中,去执行选\(K-1\)个值得子问题。
  • 这样做法的合理性在于,如果每次选则的是原来的\(D_i\),那么就是直接加入这个答案,如果选择的是某次操作后的\(D_{i-1}+D_{i+1}-D_i\),那么代表\(D_i\)在此前一定加入过答案,此时答案里累加上这个数代表,从答案中去掉原来选择的\(D_i\),加入\(D_i\)两侧的数,同样会使得选则的间距\(+1\)
  • 注意,在合并边界元素时,如果再次进行反转操作并不会使选中的段数增加,所以如果边界被合并了一次重置该位置数字时应设为正无穷。
  • 可以发现几次操作以后前驱后继就很难寻找了,所以可以采用链表来维护这个数列,同时在插入堆中时绑定链表节点的编号,此时操作即改为合并三个链表元素,标记堆中三个元素不合法,执行\(K\)次即可选出合法的\(K\)段间距,即\(K\)个点对。

\(\\\)

\(Code\)


#include<cmath>
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define R register
#define gc getchar
#define N 100010
#define inf 1000000000
using namespace std;
 
inline int rd(){
    int x=0; bool f=0; char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return f?-x:x;
}
 
bool vis[N];
int n,m,ans,pre[N],nxt[N],num[N];
priority_queue<pair<int,int> > q;
 
int main(){
    n=rd(); m=rd();
    for(R int i=2,lp=rd(),now;i<=n;++i){
        pre[i]=i-1; nxt[i]=i+1;
        num[i]=(now=rd())-lp; lp=now;
    }
    pre[2]=nxt[n]=0;
    for(R int i=2;i<=n;++i) q.push(make_pair(-num[i],i));
    while(m--){
        while(vis[q.top().second]) q.pop();
        int now=q.top().second;
        int pr=pre[now],nx=nxt[now];
        q.pop(); ans+=num[now];
        pre[nxt[now]=nxt[nx]]=now;
        nxt[pre[now]=pre[pr]]=now;
        num[now]=(pr&&nx)?min(inf,num[pr]+num[nx]-num[now]):inf;
        vis[pr]=vis[nx]=1; q.push(make_pair(-num[now],now));
    }
    printf("%d\n",ans);
    return 0;
}

\(\\\)

种树就是把链表循环起来选最大,循环的关系不需要考虑边界的特判比上面的还水

\(\\\)

#include<cmath>
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define R register
#define gc getchar
#define N 200010
#define inf 1000000000ll
using namespace std;
typedef long long ll;

inline ll rd(){
    ll x=0; bool f=0; char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return f?-x:x;
}

bool vis[N];
ll n,m,ans,pre[N],nxt[N],num[N];
priority_queue<pair<int,int> > q;

int main(){
    n=rd(); m=rd();
    if(n<m*2){puts("Error!");return 0;}
    for(R ll i=1;i<=n;++i){
        pre[i]=i-1; nxt[i]=i+1;
        num[i]=rd();
    }
    pre[1]=n; nxt[n]=1;
    for(R ll i=1;i<=n;++i) q.push(make_pair(num[i],i));
    while(m--){
        while(vis[q.top().second]) q.pop();
        ll now=q.top().second;
        ll pr=pre[now],nx=nxt[now];
        q.pop(); ans+=num[now];
        pre[nxt[now]=nxt[nx]]=now;
        nxt[pre[now]=pre[pr]]=now;
        num[now]=min(inf,num[pr]+num[nx]-num[now]);
        vis[pr]=vis[nx]=1; q.push(make_pair(num[now],now));
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2018-09-01 14:16  SGCollin  阅读(69)  评论(0编辑  收藏  举报