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

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

题目链接:P1792 [国家集训队]种树

题意

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

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

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

m ≤ n ≤ 2 × 1 0 5 m\le n \le 2\times 10^5 mn2×105 − 1000 ≤ A i ≤ 1000 -1000\le A_i\le1000 1000Ai1000

考虑反悔型贪心,维护一个大根堆,先把所有结点加进去

然后每次取出权值最大的点,把它加入答案

如何反悔呢?

我们再加入一个权值为 a l i + a r i − a i a_{l_i} + a_{r_i}-a_i ali+ariai 的节点,表示反悔结点 i i i 时的答案

不难发现这样我们再次选到 a i a_i ai 的时候,虽然选的是它,但是其实对答案的贡献是它左右两个结点。

时间复杂度 O ( m log ⁡ n ) O(m \log n) O(mlogn)

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
#include <queue>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
namespace FastIO
{
    #define gc() readchar()
    #define pc(a) putchar(a)
    #define SIZ (int)(1e6+15)
    char buf1[SIZ],*p1,*p2;
    char readchar()
    {
        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
        return p1==p2?EOF:*p1++;
    }
    template<typename T>void read(T &k)
    {
        char ch=gc();T x=0,f=1;
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
        k=x*f;
    }
    template<typename T>void write(T k)
    {
        if(k<0){k=-k;pc('-');}
        static T stk[66];T top=0;
        do{stk[top++]=k%10,k/=10;}while(k);
        while(top){pc(stk[--top]+'0');}
    }
}using namespace FastIO;
#define N (int)(2e5+25)

bool vis[N];
int n,m,a[N],l[N],r[N];
struct node{int val,id;};
bool operator<(node a,node b){return a.val<b.val;}
priority_queue<node> q;
signed main()
{
    // ios::sync_with_stdio(0);
    // cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    read(n);read(m);
    if(n<m*2)return puts("Error!"),0;
    for(int i=1; i<=n; i++)
    {
        read(a[i]);
        l[i]=i-1;r[i]=i+1;
        q.push({a[i],i});
    }
    l[1]=n; r[n]=1; int res=0;
    while(m--)
    {
        while(vis[q.top().id])q.pop();
        node u=q.top(); q.pop();
        res+=u.val;
        int id=u.id;
        vis[l[id]]=vis[r[id]]=1;
        a[id]=a[l[id]]+a[r[id]]-a[id];
        l[id]=l[l[id]];r[l[id]]=id;
        r[id]=r[r[id]];l[r[id]]=id;
        q.push({a[id],id});
    }
    write(res);
    return 0;
}

转载请说明出处

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值