P3620 [APIO/CTSC2007] 数据备份

转化题意。把相邻两个办公楼之间的距离抽象为一个点的点权,问题就变为从 N − 1 N-1 N1 个点中选择 K K K 个点,且这 K K K 个点不相邻,可以得到的最小点权和。

一种显然的贪心思路是简单地把所有点压入优先队列中,每次取出未被标记的点权最小的点累加到答案中,标记与它相邻的两个点。重复该过程 K K K 次。显然这是错误的,样例就可以 Hack。因为有可能标记的相邻点点权和比当前点和下一步匹配的远端点点权和更小。

引入一种类似于网络流中“退流”的思想,我们在取走一个最小点权点 a i a_i ai 的同时,再向优先队列中压入一个编号为 i i i,点权为 a i − 1 + a i + 1 − a i a_{i-1}+a_{i+1}-a_i ai1+ai+1ai 的点。如果该点能再次被选择,就相当于取与 a i a_i ai 相邻的点更优而不选 a i a_i ai 这个点, a i a_i ai 的点权被抵消了。

注意数组大小,数据范围要开 long long

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long

struct node
{
	int id,v;
	bool friend operator < (node a,node b){return a.v>b.v;}
};
priority_queue<node> q;
const int maxn=1e5+5;
struct link{int l,r,v;}a[maxn];
bool vis[maxn];

signed main()
{
	int N,K;cin>>N>>K;
	int tmp;cin>>tmp;
	for(int i=1;i<N;i++)
	{
		int s;cin>>s;
		a[i].v=s-tmp,tmp=s,a[i].l=i-1,a[i].r=i+1,q.push({i,a[i].v});
	}
	a[0].v=a[N].v=INT_MAX;
	ll ans=0;
	while(K--)
	{
		while(vis[q.top().id]) q.pop();
		int x=q.top().id;q.pop();
		vis[a[x].l]=vis[a[x].r]=1,ans+=a[x].v,a[x].v=a[a[x].l].v+a[a[x].r].v-a[x].v,q.push({x,a[x].v});
		a[x].l=a[a[x].l].l,a[x].r=a[a[x].r].r,a[a[x].r].l=x,a[a[x].l].r=x;
	}
	cout<<ans;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值