• bzoj 2151 种树 题解


    传送门


    【分析】

    反悔贪心

    若没有相邻不可选的限制,则用优先队列维护前 (m) 大,然后直接求和即可

    现考虑对贪心如何反悔:

    假设我们处于开局,什么都没选的状态

    当贪心选择第 (i) 个时,由于相邻不可选,需要删除 ((i-1))((i+1)) 的权值,避免产生相邻的选择

    但实际上有可能 (a_{i-1}+a_{i+1}>a_i) 从而反悔之前的贪心,故我们补回一个点,权值为 (a_{i-1}+a_{i+1}-a_i)

    若后续过程中,我们选择了该点,则代表我们反悔了选择 (a_i) 的这次贪心;但同时,我们选择的点数仍然+1

    此时,我们删除点 ((i-2))((i+2)) 的权值,同样是为了避免产生相邻的选择

    但与上面相同,实际上可能 (a_{i-2}+a_i+a_{i+2}>a_{i-1}+a_{i+1}>a_i) 从而反悔之前的贪心,故我们同样补回一个点,权值为 (a_{i-2}+a_i+a_{i+2}-a_{i-1}-a_{i+1}=a_{i-2}+a_{i+2}-(a_{i-1}-a_{i+1}-a_i))

    而这次过程等价于将上一步新产生的补充点看成同样的一个点,再进行上一步的补充操作


    因此我们不区分补充的点与原有的点,直接将原有的点塞进优先队列中,并维护一个循环链表

    当我们从优先队列中取出一个未被打过标记的点时,将它与它前后,共三个点打上标记

    之后,我们新建权值为 (a_{i-1}+a_{i+1}-a_i) 的点,并在循环队列中顶替原来的三个点即可

    执行 (m) 次得到答案


    【代码】

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    typedef double db;
    #define fi first
    #define se second
    const int MAXN=4e5+10;
    int n, m, a[MAXN], pre[MAXN], suf[MAXN], cntc;
    bool used[MAXN];
    priority_queue<pii> pq;
    inline int maxc(){
        int val, pos;
        while(pq.size()){
            val=pq.top().fi;
            pos=pq.top().se;
            pq.pop();
            if(!used[pos])
                break;
        }
        if(used[pos]) return 0;
    
        used[pos]=used[ pre[pos] ]=used[ suf[pos] ]=1;
        a[++cntc]=a[ pre[pos] ]+a[ suf[pos] ]-a[pos];
        suf[ pre[pre[pos]] ]=cntc;
        pre[ suf[suf[pos]] ]=cntc;
        pre[cntc]=pre[pre[pos]];
        suf[cntc]=suf[suf[pos]];
        pq.push( pii(a[cntc], cntc) );
        return val;
    }
    inline void init(){
        cin>>n>>m;
        for(int i=1;i<=n;++i){
            cin>>a[i];
            pre[i]=i-1;
            suf[i]=i+1;
            pq.push( pii(a[i], i) );
        }
        suf[n]=1; pre[1]=n;
        cntc=n;
    }
    int main(){
        ios::sync_with_stdio(0);
        cin.tie(0); cout.tie(0);
        init();
        if(n<m+m){
            cout<<"Error!";
        }
        else{
            int res=0;
            for(int i=1;i<=m;++i)
                res+=maxc();
            cout<<res;
        }
        cout.flush();
        return 0;
    }
    
  • 相关阅读:
    博客园博客备份
    前后端对字段去除首尾空白
    StringEscapeUtils类的转义与反转义方法
    validatebox自定义验证规则以及使用
    mybatis动态sql中的trim标签的使用
    异步IO
    tomcat_下载
    JDK__下载地址
    Eclipse_下载地址
    Linux守护进程
  • 原文地址:https://www.cnblogs.com/JustinRochester/p/15111116.html
Copyright © 2020-2023  润新知