洛谷 P1792 [国家集训队]种树

A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。

园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n。并且每个位置都有一个美观度Ai,如果在这里种树就可以得到这Ai的美观度。但由于A城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i号位置和i+1号位置叫相邻位置。值得注意的是1号和n号也算相邻位置!)。

最终市政府给园林部门提供了m棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将m棵树苗全部种上,给出无解信息。

我们每次肯定是选美观值最大的位置种,然后考虑相邻的位置不能种的限制,可以考虑反悔贪心,假如我们选了\(i\)这个位置,设\(i\)左右两边的位置为\(l_i,r_i\),我们把\(a_{r_i}+a_{l_i}-a_i\)加进去,相当于把这三个合并成一个,这样下次如果选到这个就相当于选了\(l_i,r_i\)两个位置了。

这个过程可以用双向链表轻松实现。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define mp make_pair
#define fi first
#define se second
const int N = 5e5;
using namespace std;
int n,k,nxt[N + 5],pre[N + 5],vis[N + 5];
long long ans,a[N + 5];
priority_queue <pair<long long,int> > q;
int main()
{
    scanf("%d%d",&n,&k);
    if (k > n / 2)
    {
        cout<<"Error!"<<endl;
        return 0;
    }
    for (int i = 1;i <= n;i++)
        scanf("%lld",&a[i]);
    for (int i = 1;i <= n;i++)
    {
        nxt[i] = i + 1;
        pre[i] = i - 1;
        q.push(mp(a[i],i));
    }
    pre[1] = n;
    nxt[n] = 1;
    for (int i = 1;i <= k;i++)
    {
        while (vis[q.top().se])
            q.pop();
        ans += q.top().fi;
        int u = q.top().se;
        q.pop();
        a[u] = a[pre[u]] + a[nxt[u]] - a[u];
        vis[pre[u]] = vis[nxt[u]] = 1;
        nxt[pre[pre[u]]] = u;
        pre[nxt[nxt[u]]] = u;
        pre[u] = pre[pre[u]];
        nxt[u] = nxt[nxt[u]];
        q.push(mp(a[u],u));
    }
    cout<<ans<<endl;
    return 0;
}
posted @ 2020-11-10 21:00  eee_hoho  阅读(185)  评论(0编辑  收藏  举报