洛谷P1484 种树(反悔贪心,双向链表+堆)
https://www.luogu.com.cn/problem/P1484
最近这个“反悔贪心”出现频率有点儿高。。。
如果选2堆:
如果(a[x])是最大的,那么(a[x-1])和(a[x+1])要么都不选,要么扔掉(a[x])之后同时选。
证明:
假设没选最大的(a[x]),选了(a[x-1])而没选(a[x+1])
设选的另一个是(a[k]),(a[k])和(a[x])一定不相邻,选(a[x])和(a[k])一定更优
所以就可以选出最大的(a[x])之后,删除掉(a[x-1])和(a[x+1]),然后修改(a[x])为(a[x-1]+a[x+1]-a[x]),这样当第二次选择(a[x])时,即表示不选(a[x])而改选(a[x-1])和(a[x+1]),也就是实现反悔的过程。
用双向链表和堆实现
#include<cstdio>
#include<queue>
#include<iostream>
using namespace std;
#define N 500002
long long a[N];
struct node
{
int g;
bool operator < (node p) const
{
return a[g]<a[p.g];
}
}e[N];
priority_queue<node>q;
bool tra[N];
int l[N],r[N];
int main()
{
// freopen("data.txt","r",stdin);
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
for(int i=2;i<=n;++i) l[i]=i-1;
for(int i=1;i<n;++i) r[i]=i+1;
node now;
for(int i=1;i<=n;++i)
{
now.g=i;
q.push(now);
}
long long ans=0;
while(k && !q.empty())
{
now=q.top();
q.pop();
if(a[now.g]<=0) break;
if(tra[now.g]) continue;
ans+=a[now.g];
--k;
tra[l[now.g]]=true;
tra[r[now.g]]=true;
a[now.g]=a[l[now.g]]+a[r[now.g]]-a[now.g];
l[now.g]=l[l[now.g]];
r[l[now.g]]=now.g;
r[now.g]=r[r[now.g]];
l[r[now.g]]=now.g;
q.push(now);
}
printf("%lld",ans);
}