国庆六日游——第一天第一题

国庆六日游——第一天第一题

普及组请出门右转
萌新出门左转

正题:

一场天灾过后,B市的所有主干道路都被切断了。

灾后重建的一项重要任务是恢复通信。B市共有n个关键的据点,而我们现在有一条关键的消息,需要所有的据点都要收到。

消息的传递有两种方式:

空降:可以直接将消息传给某个据点,每次需要的代价为v
通信员:可以将消息从一个据点传到另一个据点,需要的代价为两个据点在地图上的欧氏距离的平方
注意,通信员只能从已有消息的据点传递消息到另一个据点。所以,至少第一个收到消息的据点一定是通过空降的。
在保证所有的据点都收到消息的前提下,最小的总代价是多少?

输入的第一行包含空格隔开的两个数n,v。

接下来n行,每行有两个空格隔开的数x,y,表示每个据点在地图上的坐标。

输出一行,仅包含一个整数,表示最小的总代价。

保证n <= 5000, v <= 100000, x, y <= 30000;

时限 1000ms 空间限制 128MB

样例:
6 1000
0 0
0 10
20 20
30 30
80 100
100 100

输出:

3200

思路

假如没有“空降”这个条件,这题显然就是坐标转换最小生成树。

“最小的总代价”对应“最小”, 所有据点收到消息 对应 生成树

可是题目连“边”和“边权”都没有出现, 哪儿来的图?

自己YY据点和据点两两连线,连成一个完全图,边权为两点之间的欧氏距离。

辣么怎么处理“空降”的条件呢?

处理不了,告辞

这时候我们就需要拿出我们万能的笔和纸,使出必杀技——画一画!
(手动求生欲

以样例为例,画出来应该是这样:
在这里插入图片描述
GeoGebra生成图片
经过看题解仔细观察,细心的人可能发现,空降的次数等于图中联通块的个数。
看到这里,大概会有人和我一样开始考虑求联通块的方法。

但这种思路有一个本质的问题——我们首先要得到这个图,然后才能求联通块。
一种解决办法是把所有边权大于v的边全部换成v,然后再用Kruskal求解

撒花完结!

看看数据范围

n <= 5000

笑容逐渐消失

Kruskal : 我太难了

Prim : 终于到我出场了

把边权换成v的本质是什么?

复读机

观察下面的图:

在这里插入图片描述
于是,我们可以加入一个虚拟节点,这个节点到每一个点的距离都是v

但是,先别急着加边!如果我们从虚拟节点开始“搜索”,会发现它每一条出边的权值都是v(废话),我们可以省去加虚拟边的过程,直接把每个点的dist初始化为v。

(以上看不懂可以先复习一下Prim算法)

上代码:

#include<bits/stdc++.h>
#define int long long
//以下是读入输出优化,看不懂可以直接用cin/scanf, cout/printf代替
#define MAX_INPUT maxi
using namespace std;
namespace IO{
   
    const int MAX_INPUT = 1000000;
    #define getc() (p1 == p2 && (p2 = (p1 = buf) + inbuf -> sgetn(buf, MAX_INPUT), p1 == p2) ? EOF : *p1++)
    char buf[MAX_INPUT], *p1, *p2;
    template <typename T> inline bool redi(T &x) {
   
        static std::streambuf *inbuf = cin.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值