EVERYTHING HAPPENS FOR THE BEST!|

wnsyou

园龄:2年4个月粉丝:19关注:16

2023-05-17 23:49阅读: 21评论: 0推荐: 0

abc270_f Transportation 题解

Transportation

题意

n 个城市,你可以执行以下操作若干次:

  • 选择一个没有建机场的城市 i,花费 xi 建一个机场。
  • 选择一个没有建港口的城市 i,花费 yi 建一个港口。

还有 m 条没有修建的道路,第 i 条道路双向连接 aibi,修建这条道路需要花费 zi

两个城市 uv 直接可达当且仅当:

  • uv 都有机场。
  • uv 都有港口。
  • uv 直接有一条道路。

求最小花费,使得从任意一个城市 u 可以在经过若干城市后抵达任意一个城市 v

思路

这题只考建图和最小生成树。

初步考虑

首先,不看机场和港口,那么就变成了一个最小生成树问题。

特殊情况

那么有机场呢?画张草图看看吧。

上面那种建图方式,就是在两个有机场的城市间建边,不仅难以维护花费,你也不知道有多少个城市建了机场,建图方式不够优秀。

优化建图

这种建图方式值得思考。

根据题意,两个有机场的城市可以互相抵达,相当于有一个机场聚集地,设立机场的城市可以来到这个聚集地,并从这里走向另外一个设立机场的城市。

那么维护起来就很方便了,假定机场聚集地在 n+1,那么就可以把 i=1,2n 中的每个 i 都向 n+1 建立一条候选边,边权为 xi

上图就会变成:

港口同理,将聚集地设置为与机场聚集地不同的一个即可(也不能和任何一座城市下标相同),建议设为 n+2

最后思考

可我并不知道是否要通过建立机场或港口来互相抵达啊?

这个也不难,枚举是否建立机场和是否建立港口,每次跑一遍克鲁斯卡尔,求出 4 种情况中花费最小值即可。

复杂度

  • 时间:O((n+m)log(n+m))
  • 空间:O(n+m)

Code

点击查看代码
#include <iostream>
#include <algorithm>
using namespace std;
using ll = long long;
const int N = 2e5 + 10, E = 6e5 + 10;
struct Edge {
int x, y, z;
bool operator < (const Edge &i) const {
return z < i.z;
}
} e[N], a[E];
int n, m, x[N], y[N], fa[N], ae;
ll ans = 1e18;
int Find (int x) {
return (fa[x] ? fa[x] = Find(fa[x]) : x);
}
ll kruskal (bool f1, bool f2) { // 克鲁斯卡尔算法
ll sum = 0;
int num = 0;
sort(a + 1, a + ae + 1);
for (int i = 1; i <= ae; i++) { // 这些不用说了吧
int l = Find(a[i].x), r = Find(a[i].y);
if (l != r) {
sum += a[i].z, num++, fa[l] = r;
}
}
if (num == n + f1 + f2 - 1) { // 注意!题目并不保证在只走普通道路的情况下一定合法,需判断
return sum;
}
return 1e18;
}
int main () {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> x[i];
}
for (int i = 1; i <= n; i++) {
cin >> y[i];
}
for (int i = 1; i <= m; i++) {
cin >> e[i].x >> e[i].y >> e[i].z;
}
for (int i = 0; i < 2; i++) { // 是否建立机场
for (int j = 0; j < 2; j++) { // 是否建立港口
for (int k = 1; k <= n + 2; k++) {
fa[k] = 0; // 先清空
}
ae = 0;
for (int k = 1; k <= m; k++) { // m 条道路
a[++ae] = e[k];
}
if (i) { // 走机场
for (int k = 1; k <= n; k++) {
a[++ae] = {k, n + 1, x[k]};
}
}
if (j) { // 走港口
for (int k = 1; k <= n; k++) {
a[++ae] = {k, n + 2, y[k]};
}
}
ans = min(ans, kruskal(i, j));
}
}
cout << ans;
return 0;
}

本文作者:wnsyou の blog

本文链接:https://www.cnblogs.com/wnsyou-blog/p/17410723.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   wnsyou  阅读(21)  评论(0编辑  收藏  举报
  1. 1 勝利への道 安藤浩和
  2. 2 Minecraft’s End Eric Fullerton
  3. 3 月光曲完整版 贝多芬 云熙音乐
  4. 4 平凡之路 (Live版) 朴树
  5. 5 Minecraft C418
  6. 6 Paradise NiziU
  7. 7 叫我,灰原哀 龙大人不喷火
  8. 8 心机之蛙,一直摸你肚子 ——《名侦探柯南》原创同人曲 炊饭,叶辞樱,温海,寒砧,南柯柯,小茜玛姬,盛姝,阿崔Ac,贝壳初,千湛,兮茶子DaYu,乔慕,黎鹿北,起千温卿,遮阳伞,曲悠
  9. 9 战 歌 此去经年
勝利への道 - 安藤浩和
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

暂无歌词

加载中…

{{tag.name}}

{{tran.text}}{{tran.sub}}
无对应文字
有可能是
{{input}}
尚未录入,我来提交对应文字
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示