BZOJ1150 [CTSC2007]数据备份Backup

这是一道很好的题目,正常人都想不出做法。

我还记得题解是说:

(1)想到动规,但是T到死。。。

(2)转化成网络流,还是T的不行

(3)咦,好像是贪心欸,做出来了(你在卖萌!)

其实算法很简单,首先我们知道必须找相邻的两个进行配对,但是不是直接找最小,而是每次要找最短的一段(后面会解释什么叫"段"),于是可以用堆来维护。

具体做法是找出当前最短的段X,设左边那段叫L,右边那段叫R,那么:

只要(1)ans += dis[X]  (2)删除X、L、R三段,即堆中删除dis[X], dis[L], dis[R] (3)堆中加入dis[L] + dis[R] - dis[X]即可

删除我用的是时间标记法,比较容易实现。

什么叫段:就是i到j之间的全部都两两配对形成最小值的区间的啊。。

一开始有n - 1段,每次操作会造成减少两段,这是合理的,下面会解释。

算法合理性解释:

如果你取了一段X,但是其实取左右L和R比现在更优,那么下一次你会取L + R - X这一"段", 最后的ans = dis[L] + dis[R],也就是取了L和R。

这就解释了为什么会操作一次减少两段,因为后面如果选了dis[L] + dis[R] - dis[X]这段等于一下子选了两段,所以前面就会先减掉1段。

 1 #include <cstdlib>
 2 #include <cstdio>
 3 #include <queue>
 4 #include <utility>
 5 #include <cstring>
 6 #include <vector>
 7
 8 const int arr = 200000;
 9 using namespace std;
10 typedef pair<int, pair <int, int> > PP;
11
12 priority_queue <PP> heap;
13 int n, K, ans, L, R, pos, l[arr], r[arr], t[arr], a[arr];
14 bool v[arr];
15
16 int main(){
17     int maxn = 2000000000;
18     scanf("%d%d", &n, &K);
19     for (int i = 1; i <= n; ++i)
20         scanf("%d", a + i);
21     for (int i = n; i > 1; --i)
22         a[i] -= a[i - 1];
23
24     a[1] = a[++n] = maxn;
25
26     while (!heap.empty()) heap.pop();
27     for (int i = 1; i <= n; ++i){
28         heap.push(make_pair(-a[i], make_pair(i, 1)));
29         l[i] = i - 1, r[i] = i + 1, t[i] = 1;
30     }
31     memset(v, 0, sizeof(v));
32
33     PP x;
34     ans = 0;
35     while (K--){
36         for (x = heap.top(); v[x.second.first] || x.second.second != t[x.second.first]; heap.pop(), x = heap.top());
37         x = heap.top();
38         ans -= x.first;
39         pos = x.second.first;
40         L = l[pos], R = r[pos];
41         r[L] = r[R];
42         l[r[R]] = L;
43         v[pos] = v[R] = 1;
44         a[L] += a[R] - a[pos];
45         heap.push(make_pair(-a[L], make_pair(L, ++t[L])));
46     }
47     printf("%d\n", ans);
48     return 0;
49 }

非常丑陋的代码。。。正常人看不懂系列。。(话说hzwer:"堆的精确删点我还不会,于是就用SBT做了。。。",简直超神。。。)

时间: 2024-10-16 05:06:01

BZOJ1150 [CTSC2007]数据备份Backup的相关文章

BZOJ1150 [CTSC2007]数据备份Backup 【堆 + 链表】

题目 你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份.然而数据备份的工作是枯燥乏味 的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏的乐趣.已知办公 楼都位于同一条街上.你决定给这些办公楼配对(两个一组).每一对办公楼可以通过在这两个建筑物之间铺设网 络电缆使得它们可以互相备份.然而,网络电缆的费用很高.当地电信公司仅能为你提供 K 条网络电缆,这意味 着你仅能为 K 对办公楼(或总计2K个办公楼)安排备份.任一个办公楼都属于唯一

【链表】bzoj 1150: [CTSC2007]数据备份Backup

1150: [CTSC2007]数据备份Backup Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1136  Solved: 458[Submit][Status][Discuss] Description Input 输入的第一行包含整数n和k,其中n(2 ≤ n ≤100 000)表示办公楼的数目,k(1≤ k≤ n/2)表示可利用的网络电缆的数目.接下来的n行每行仅包含一个整数(0≤ s ≤1000 000 000), 表示每个办公楼到大

【BZOJ 1150】 1150: [CTSC2007]数据备份Backup (贪心+优先队列)

1150: [CTSC2007]数据备份Backup Description 你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份.然而数据备份的工作是枯燥乏味 的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏的乐趣.已知办公 楼都位于同一条街上.你决定给这些办公楼配对(两个一组).每一对办公楼可以通过在这两个建筑物之间铺设网 络电缆使得它们可以互相备份.然而,网络电缆的费用很高.当地电信公司仅能为你提供 K 条网络电缆,这意味 着你仅

1150: [CTSC2007]数据备份Backup

1150: [CTSC2007]数据备份Backup https://lydsy.com/JudgeOnline/problem.php?id=1150 分析: 堆+贪心. 每次选最小的并一定是最优的,如果不选这个最小的,那一定是为了取它左右两边(两条都要取才可能比当前优). 如果先选了最小的,考虑后面如何撤销.选了这个后,左右两边的线段就要删了,那么在加入一个长度为:左边的长度+右边的长度-当前的线段的长度 的一条线段. wqs二分,模拟费用流 代码: 1 #include<cstdio>

【bzoj1150】[CTSC2007]数据备份Backup 模拟费用流+链表+堆

题目描述 你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份.然而数据备份的工作是枯燥乏味的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏的乐趣.已知办公楼都位于同一条街上.你决定给这些办公楼配对(两个一组).每一对办公楼可以通过在这两个建筑物之间铺设网络电缆使得它们可以互相备份.然而,网络电缆的费用很高.当地电信公司仅能为你提供 K 条网络电缆,这意味着你仅能为 K 对办公楼(或总计2K个办公楼)安排备份.任一个办公楼都属于唯一的配

bzoj1150 [CTSC2007]数据备份

Description 你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份.然而数据备份的工作是枯燥乏味的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏的乐趣.已知办公楼都位于同一条街上.你决定给这些办公楼配对(两个一组).每一对办公楼可以通过在这两个建筑物之间铺设网络电缆使得它们可以互相备份.然而,网络电缆的费用很高.当地电信公司仅能为你提供 K 条网络电缆,这意味着你仅能为 K 对办公楼(或总计2K个办公楼)安排备份.任一个办公楼

bzoj 1150: [CTSC2007]数据备份Backup

Description 你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份.然而数据备份的工作是枯燥乏味 的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏的乐趣.已知办公 楼都位于同一条街上.你决定给这些办公楼配对(两个一组).每一对办公楼可以通过在这两个建筑物之间铺设网 络电缆使得它们可以互相备份.然而,网络电缆的费用很高.当地电信公司仅能为你提供 K 条网络电缆,这意味 着你仅能为 K 对办公楼(或总计2K个办公楼)安排备份.任一

【bzoj1150】[CTSC2007]数据备份Backup

将k对点两两相连,求最小长度 易证得,最优方案中,相连的办公楼一定是取相邻的比取不相邻的要更优 然后就可以用贪心来做这道题了.. 将初始所有的线段放进堆里 每次取最短的线段进行连接,且ans+=a[i] 取完后删除当前线段,与相邻的两条线段,同时再插入新边,权值为a[pre]+a[next]-a[now] 其作用与最大流中的反向弧有点像,下一次若取到这条边,即ans+=a[pre]+a[next]-a[now] 很明显a[now]与之前抵消了,即不取now,反而取相邻的两条边去了 1 #incl

bzoj1150: [CTSC2007]数据备份Backup--贪心+优先队列维护堆

题目大意:将k对点两两相连,求最小长度 易证得,最优方案中,相连的办公楼一定是取相邻的比取不相邻的要更优 然后就可以用贪心来做这道题了.. 之前向CZL大神学习了用堆来贪心的做法orz 大概思路就是将初始所有的线段放进堆里 每次取最短的线段进行连接,且ans+=a[i] 取完后删除当前线段,与相邻的两条线段,同时再插入新边,权值为a[pre]+a[next]-a[now] 其作用与最大流中的反向弧有点像,下一次若取到这条边,即ans+=a[pre]+a[next]-a[now] 很明显a[now