题解 P1484 【种树】
P1792题解
传送门
题意简述
给出一个 n 个有权值节点的链,选出 k 个不相邻的节点,使它们的权值和最大。
题目分析
如果 k = 1,那么结果就是
如果 k = 2,那么就有两种情况:
- 选择最大值
a_i ,不选a_{i-1} ,a_{i+1} ,再从剩下的数中选择最大值。 - 不选最大值
a_i ,选a_{i-1} ,a_{i+1} 。
为什么不选最大值
如果只选了
显然
所以最小值左右两侧的数要么都选,要么都不选。
所以我们可以先选上
代码实现思路
所以我们可以建立一个链表 Q,分别记录
每次取出堆顶,更新答案。再删除节点(链表中打标记、更新左右节点数组,大根堆中删除)、插入新节点。
执行
易错点
- 这是一条链。
- 记得开 long long 。
AC代码
内带详细注释
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#include<cmath>
#define N 500010
#define ll long long
using namespace std;
void read(ll &x){
int f=1;x=0;
char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
x*=f;
}
ll a[N],le[N],r[N];
long long ans;
bool v[N];
priority_queue < pair < ll , ll > > q;
void del(int x){//删除操作
le[r[x]]=le[x];
r[le[x]]=r[x];
v[x]=1;//节点是否删除
}
int main(){
int n,m;
cin>>n>>m;
//le[1]=n;r[1]=2;le[n]=n-1;r[n]=1;
for(int i=1;i<=n;i++){
read(a[i]);//读入
}
for(ll i=1;i<=n;i++) le[i]=i-1,r[i]=i+1;//建立链表
for(ll i=1;i<=n;i++){
q.push(make_pair(a[i],i));//第一元为值,第二元为标号
}
while(m--){
int x,y;
while(v[q.top().second]) q.pop();
y=q.top().second;q.pop();//取出最大节点
if(a[y]<0) break;
ans+=a[y];//更新答案
a[y]=a[le[y]]+a[r[y]]-a[y];//新节点
del(le[y]);del(r[y]);//删除
q.push(make_pair(a[y],y));
}
printf("%lld\n",ans);
return 0;
}