洛谷P1484 种树
题目描述
cyrcyr今天在种树,他在一条直线上挖了n个坑。这n个坑都可以种树,但为了保证每一棵树都有充足的养料,cyrcyr不会在相邻的两个坑中种树。而且由于cyrcyr的树种不够,
他至多会种k棵树。假设cyrcyr有某种神能力,能预知自己在某个坑种树的获利会是多少(可能为负),请你帮助他计算出他的最大获利。
数据范围
对于20%的数据,n<=20。
对于50%的数据,n<=6000。
对于100%的数据,n<=500000,k<=n/2,在一个地方种树获利的绝对值在1000000以内。
解题思路
我们考虑维护一个优先队列,使得其中的数按照严格单调递减的方式排序。 对于初始化而言,我们将每个点的获利放到优先队列中。
在操作中,每次取出堆顶,对于这个堆顶id我们有两个选择,将其加入选择,或者将它的两端都加入选择。不可能存在一种情况使得两端某个单独加入(如果这样两端id才是堆顶)
对于上述操作的实现是这样的,我们先将该点值直接加入总和中,然后将该点两端删去,把该点的值修改为两端值之和减去原来的该点的值,我们考虑以下几种情况
1.val[x]>=val[l[x]]+val[r[x]]
在这种情况下表示选择该点是最优的抉择,再也不可能影响最后总和结果.
2.val[x]<va[l[x]]+val[r[x]].
有两个可能
A.它在最后排序中位于可以影响结果的位置,表示对于之前做的决定反悔。
B.反之表示虽然两端和大于该点值,但是选择此点对于后续的影响使结果更优。
附上AC代码
#include<iostream> #include<cstring> #include<cstdio> #include<queue> #define reg register using namespace std; const int Maxn=5e5+5; typedef long long ll; struct dug{ int g,val; bool operator <(const dug &rhs) const { return val<rhs.val; } }; ll ans=0,val[Maxn]; int n,k,fin=0; bool sbp[Maxn]; priority_queue<dug> q; int l[Maxn],r[Maxn]; int main(){ memset(sbp,0,sizeof(sbp)); scanf("%d %d",&n,&k); for(int i=1;i<=n;++i){ l[i]=i-1; r[i]=i+1; scanf("%lld",&val[i]); q.push((dug){i,val[i]}); } r[0]=1; l[n+1]=n; while(k--){ while(sbp[q.top().g])q.pop(); dug fr=q.top(); if(q.top().val<0) break; q.pop(); ans+=fr.val; int id=fr.g; sbp[r[id]]=sbp[l[id]]=1; val[id]=val[l[id]]+val[r[id]]-val[id]; q.push((dug){fr.g,val[id]}); l[id]=l[l[id]];r[l[id]]=id; r[id]=r[r[id]];l[r[id]]=id; } printf("%lld",ans); return 0; }