• [ CTSC 2007 / BZOJ 2151 ] Backup / 种树


    (\)

    (Description)


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

    • (Nin [0,10^5])(Kin [0,frac{N}{2}])(A_iin [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
    ",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
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    第三次作业
    读大道至简第三章有感
    AlphaMobileControls介绍
    wince下GetManifestResourceStream得到的Stream是null的解决
    Wince下实现ImageButton
    windows phone 8的新特性
    没有开发者账号,如何解锁wp8设备
    windows phone8手机玩玩
    windows phone7开发环境配置错误
    如何解决office2007每次打开都要正在配置
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9570191.html
Copyright © 2020-2023  润新知