【BZOJ2151】种树(贪心)

【BZOJ2151】种树(贪心)

题面

BZOJ

题解

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

#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;
}

posted @ 2018-07-25 19:47  小蒟蒻yyb  阅读(657)  评论(1编辑  收藏  举报