专栏文章

题解:P14374 [JOISC 2018] 糖果 / Candies

P14374题解参与者 2已保存评论 1

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
1 条
当前快照
1 份
快照标识符
@min1t5r0
此快照首次捕获于
2025/12/01 19:10
3 个月前
此快照最后确认于
2025/12/01 19:10
3 个月前
查看原文
此题为 P1792 [国家集训队] 种树 的升级版,与原题的区别是此题的序列不是环形的。
考虑使用反悔贪心 + 双向链表
朴素的贪心是每次选择收益最高的点,并给它左右的节点打上 tag。但有可能出现左右的节点都选的收益比只选这个节点的收益高的情况。
所以我们需要反悔贪心,用大根堆维护当前可进行的操作,选择节点 xx 时,删除左右的节点,更新链表,把 xx 的权值赋为 vl+vrvxv_l + v_r - v_x,并加入堆。再次选到节点 xx 时即为放弃节点 xx,选择 xx 节点的左右节点。
对于不是环形这个问题,其实很好解决,只要将 v0v_0vn+1v_{n+1} 赋为 - \infty 就可以避免选到 00n+1n + 1 的情况。
要记得开 long long,否则过不去样例 2。别问我是怎么知道的

AC CODE

CPP
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
ll ans; 
priority_queue<pair<ll,int>>q;
struct node{
	ll val;
	int l,r;
}a[200005];
bool tag[200005];
void del(int x)
{
	a[x].l=a[a[x].l].l;
	a[x].r=a[a[x].r].r;
	a[a[x].l].r=x;
	a[a[x].r].l=x;
}
int main()
{
	cin>>n;
	m=(n+1)/2;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i].val);
		a[i].l=i-1;
		a[i].r=i+1;
		q.push({a[i].val,i});
	}
	a[0].val=a[n+1].val=-1145141919810000;
	for(int i=1;i<=m;i++)
	{
		while(tag[q.top().second])q.pop();
		auto x=q.top();q.pop();
		ans+=x.first;
		tag[a[x.second].l]=tag[a[x.second].r]=1;
		a[x.second].val=a[a[x.second].l].val+a[a[x.second].r].val-a[x.second].val;
		q.push({a[x.second].val,x.second});
		del(x.second);
		printf("%lld\n",ans);
	}
	return 0;
}

评论

1 条评论,欢迎与作者交流。

正在加载评论...