【BZOJ2151】种树(贪心)

题面

BZOJ

题解

如果没有相邻不能选的限制,那么这就是一道傻逼题。
只需要用一个堆维护一下就好了。
现在加上了相邻点的限制,那么我们就对于当前位置加入一个撤销操作。
怎么撤销呢?
如果我们选择了一个点,那么我们就把他前后两个位置删去,
然后将当前点合并为 a [ l a s t ] + a [ n e x t ] a [ n o w ] 的权值。
如果下次选择了这个位置的话,就可以认为撤销了选择 n o w 这个位置,
转而选择了相邻的两个位置。
用一个链表维护前驱后继,堆维护答案即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define pr pair<int,int>
#define mp(x,y) (make_pair(x,y))
#define MAX 200200
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int nt[MAX],lt[MAX],n,m,a[MAX],ans;
priority_queue<pr> Q;
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)a[i]=read();
    for(int i=1;i<n;++i)nt[i]=i+1;nt[n]=1;
    for(int i=2;i<=n;++i)lt[i]=i-1;lt[1]=n;
    for(int i=1;i<=n;++i)Q.push(mp(a[i],i));
    if(m>n/2){puts("Error!");return 0;}
    while(m)
    {
        pr u=Q.top();Q.pop();
        int v=u.second;
        if(nt[lt[v]]!=v)continue;
        a[v]=a[lt[v]]+a[nt[v]]-a[v];
        lt[v]=lt[lt[v]];nt[v]=nt[nt[v]];
        nt[lt[v]]=v;lt[nt[v]]=v;
        Q.push(mp(a[v],v));ans+=u.first;
        --m;
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_30974369/article/details/81208977
今日推荐