[Luogu P3620] [BZOJ 1150] [APIO CTSC 2007]数据备份

14 篇文章 0 订阅
7 篇文章 0 订阅
洛谷传送门
BZOJ传送门

题目描述

你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份。然而数据备份的工作是枯燥乏味的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏的乐趣。

已知办公楼都位于同一条街上。你决定给这些办公楼配对(两个一组)。每一对办公楼可以通过在这两个建筑物之间铺设网络电缆使得它们可以互相备份。

然而,网络电缆的费用很高。当地电信公司仅能为你提供 K K K 条网络电缆,这意味着你仅能为 K K K 对办公楼(或总计 2 K 2K 2K 个办公楼)安排备份。任一个办公楼都属于唯一的配对组(换句话说,这 2 K 2K 2K 个办公楼一定是相异的)。

此外,电信公司需按网络电缆的长度(公里数)收费。因而,你需要选择这 K K K 对办公楼使得电缆的总长度尽可能短。换句话说,你需要选择这 K K K 对办公楼,使得每一对办公楼之间的距离之和(总距离)尽可能小。

下面给出一个示例,假定你有 5 5 5 个客户,其办公楼都在一条街上,如下图所示。这 5 5 5 个办公楼分别位于距离大街起点 1 k m , 3 k m , 4 k m , 6 k m 1km, 3km, 4km, 6km 1km,3km,4km,6km 12 k m 12km 12km 处。电信公司仅为你提供 K = 2 K=2 K=2 条电缆。

img

上例中最好的配对方案是将第 1 1 1 个和第 2 2 2 个办公楼相连,第 3 3 3 个和第 4 4 4 个办公楼相连。这样可按要求使用 K = 2 K=2 K=2 条电缆。第 1 1 1 条电缆的长度是 3 k m − 1 k m = 2 k m 3km-1km = 2km 3km1km=2km,第 2 2 2 条电缆的长度是 6 k m − 4 k m = 2 k m 6km-4km = 2 km 6km4km=2km。这种配对方案需要总长 4 k m 4km 4km 的网络电缆,满足距离之和最小的要求。

输入输出格式

输入格式:

输入文件的第一行包含整数 n n n k k k,其中 n ( 1 ≤ n ≤ 100000 ) n(1≤n≤100 000) n(1n100000)表示办公楼的数目, k ( 1 ≤ k ≤ n / 2 ) k(1≤k≤n/2) k(1kn/2)表示可利用的网络电缆的数目。

接下来的 n n n 行每行仅包含一个整数( 0 ≤ s ≤ 1000000000 0≤s≤1000 000 000 0s1000000000), 表示每个办公楼到大街起点处的距离。这些整数将按照从小到大的顺序依次出现。

输出格式:

输出文件应当由一个正整数组成,给出将 2 K 2K 2K 个相异的办公楼连成 K K K 对所需的网络电缆的最小总长度。

输入输出样例

输入样例#1:
5 2 
1 
3 
4 
6 
12 
输出样例#1:
4

说明

30 % 30\% 30%的输入数据满足 n ≤ 20 n≤20 n20

60 % 60\% 60%的输入数据满足 n ≤ 10000 n≤10 000 n10000

解题分析

神题啊QAQ… 感觉网上题解写的贪心等等的理解都不够透彻, 这道题的本质应该是二分图。

如果光从 d p dp dp来考虑, 我们显然可以得到一个 O ( n k ) O(nk) O(nk) d p dp dp, 然而似乎并不能优化…

然后我们发现这道题”两段不能相邻“的要求似乎很像给线段染色? 于是可以画个二分图:

其中C代表容量(Capacity)

然后就成了一个带权二分匹配的问题…

然而 n = 100000 n=100000 n=100000, 过不了?

我们来看一下选定一条边后的情况。 假如 3 → 2 3\to 2 32这条边最优, 而我们要进行增广:

我们发现, 现在要选 1 → 2 1\to 2 12的边, 就一定要选 3 → 4 3\to 4 34的边, 而且这样等于增加的代价为 c o s t ( 1 , 2 ) − c o s t ( 2 , 3 ) + c o s t ( 3 , 4 ) cost(1,2)-cost(2,3)+cost(3,4) cost(1,2)cost(2,3)+cost(3,4)

因为我们需要得到最小的费用, 联想到用小根堆来搞这个操作。 每次取出堆顶的那条边, 将原来其两旁的边删除, 合并成一个新的点, 再 p u s h push push进堆里。 我们可以用链表来维护每条边临近两条边的权值。

注意到 1 1 1号点只有一个出边, 所以如果某一次取到了 1 → 2 1\to2 12的这条边, 我们就不再考虑从它的反向边增广的情况, 即不再加入堆中了(具体操作可以将这条边的权值赋为一个极大值)。同理, 8 8 8号点只有一条入边, 所以按照我们贪心的思想, 先加入的权值更小, 我们同样不再考虑反向边的情况。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <queue>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 100500
#define INF 1000000005
#define mp std::make_pair
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
ll dat[MX];
int lef[MX], rig[MX], pos[MX];
struct INFO {int pos; ll val;};
IN bool operator < (const INFO &x, const INFO &y) {return x.val > y.val;}
std::priority_queue <INFO> pq;
INFO p;
int n, k, ans;
int main(void)
{
	int pre, nex;
	in(n), in(k);
	for (R int i = 1; i <= n; ++i) in(pos[i]);
	for (R int i = 1; i < n; ++i) dat[i] = pos[i + 1] - pos[i], lef[i] = i - 1, rig[i] = i + 1, pq.push({i, dat[i]});
	rig[n - 1] = 0;
	W (k--)
	{
		W (!pq.empty() && dat[pq.top().pos] != pq.top().val) pq.pop();
		if(pq.empty()) break;
		p = pq.top(); pq.pop();
		ans += p.val;
		pre = lef[p.pos], lef[p.pos] = lef[pre], rig[lef[p.pos]] = p.pos;
		nex = rig[p.pos], rig[p.pos] = rig[nex], lef[rig[p.pos]] = p.pos;
		dat[p.pos] = (pre && nex) ? std::min(INF, dat[pre] + dat[nex] - dat[p.pos]) : INF;
		dat[pre] = dat[nex] = INF; pq.push({p.pos, dat[p.pos]});
	}
	printf("%d", ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值