D. 交通规划

原题

题目描述

N N N 个沿海城市,张三需要规划所有城市的交通,可以规划三种交通方式:

对于每个城市 1 ≤ i ≤ N 1\leq i\leq N 1iN,可以花 X i X_i Xi 元建一个机场
对于每个城市 1 ≤ i ≤ N 1\leq i\leq N 1iN,可以花 Y i Y_i Yi 元建一个港口
M M M 条待建设公路,对于 1 ≤ i ≤ M 1\leq i\leq M 1iM,可以花 Z i Z_i Zi 元在城市 A i A_i Ai B i B_i Bi 之间建一条双向公路
如果两个城市 u , v u,v u,v 满足下列条件之一,则 u , v u,v u,v 可以互相到达:

  • u , v u,v u,v 都有机场
  • u , v u,v u,v 都有港口
  • u u u v v v 之间有公路
    问至少花多少代价才能让任意两个城市互相连通(可以通过中转城市)

输入格式

N   M N~M N M
X 1 X 2 ⋯ X N X_1 X_2 \cdots X_N X1X2XN
Y 1 Y 2 ⋯ Y N Y_1 Y_2 \cdots Y_N Y1Y2YN
A 1 B 1 Z 1 A_1 B_1 Z_1 A1B1Z1
A 2 B 2 Z 2 A_2 B_2 Z_2 A2B2Z2
… \dots
A M B M Z M A_M B_M Z_M AMBMZM

输出格式

输出最少的花费

样例

输入样例#1
4 2
1 20 4 7
20 2 20 3
1 3 5
1 4 6
输出样例#1
16
输入样例#2
3 1
1 1 1
10 10 10
1 2 100
输出样例#2
3
输入样例#3
7 8
35 29 36 88 58 15 25
99 7 49 61 67 4 57
2 3 3
2 5 36
2 6 89
1 6 24
5 7 55
1 3 71
3 4 94
5 6 21
输出样例#3
160

数据范围与提示

1 ≤ A i < B i ≤ N , 1 ≤ M 1\leq A_i < B_i \leq N,1\leq M 1Ai<BiN,1M
N , M ≤ 2 × 1 0 5 N,M\leq 2\times 10^5 N,M2×105
1 ≤ X i , Y i , Z i ≤ 1 0 9 1\leq X_i,Y_i,Z_i\leq 10^9 1Xi,Yi,Zi109
保证不存在重边

解法

由题意可知:要求选出图上权值和最小的边集 V V V,使得所有点两两联通
这让我们想起了什么?
最小生成树

这里有三种情况:
轮船,飞机,公路
其中两个城市通过轮船和飞机的方式相似,都是要选择点
两个城市公路的方式是要选择边
边点各有方法统计
于是暴力的方案一、二诞生了

方案一

暴力选择边(公路),选完后缩点,暴力跑点(轮船和飞机)
不过缩点很麻烦,可以优化

方案二

就是方案中缩点升级为并查集

但是这两种方法实在是麻烦,速度又慢,数据是 N , M ≤ 2 × 1 0 5 N,M\leq 2\times 10^5 N,M2×105,理论最快速度 O ( n 2 ) O(n^2) O(n2),绝对超时

不难发现,问题就出在:港口、机场、公路的分配上

如果我们能将三者统一,都视作选择点或选择边,那么题目的难度就大大降低了

由题意可知:要求选出图上权值和最小的边集 V V V,使得所有点两两联通
这让我们想起了什么?
最小生成树

1.选择边

最小生成树就是专门处理选择边的

由于是稀疏图,kruskal足以应对,以下讲解的方法都是kruskal

方案三

我们可以思索一下:飞机要走天路,轮船要走水路

不妨把天空视作一个城市,海洋也视作一个城市

因此,要使一架飞机能够从天空中到达城市 a a a或从城市 a a a去往天空中,代价是 X i X_i Xi

于是我们可以将天空视作一个超级节点 n + 1 n+1 n+1,每个节点都有一条与 n + 1 n+1 n+1的边,边权是 X i X_i Xi

轮船也是一样

但如果直接加进这两个点跑最小生成树的话,仍然有问题

举个例子

3 1
1 1 1
10 10 10
1 2 100

答案:

3

正确的走法是这样的
在这里插入图片描述
可直接跑可能会跑出这样的结果
在这里插入图片描述
也就意味着,两个超级节点并不一定在最小生成树中
所以,要讨论两个超级节点是否在最小生成树中,共四种情况
即便是两个超级节点都在最小生成树里的情况依然会出现“废边(多余的边)”
但在另三种中一定存在没有“废边”的情况
用kruskal跑四遍即可

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 7, md = 1e9 + 7;
int n, m, x[N], y[N];
struct edge {
    int u, v, w;
    bool operator<(const edge b)
    {
        return w < b.w;
    }
} e[N << 2], e1[N << 2];
int cnt;
void addE(int u, int v, int w)
{
    e[++cnt] = { u, v, w };
}
int fa[N<<1];
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
int kruskal()
{
    sort(e + 1, e + cnt + 1);
    for (int i = 1; i <= n + 2; i++)
        fa[i] = i;
    int sum = 0;
    for (int i = 1; i <= cnt; i++) {
        int u = e[i].u, v = e[i].v, w = e[i].w;
        u = find(u), v = find(v);
        if (u == v)
            continue;
        if (u < v)
            swap(u, v);
        fa[u] = v;
        // cout << " " << w << endl;
        sum += w;
    }
    // cout << sum << endl;
    for (int i = 1; i <= n; i++)
        if (find(i) != 1)
            return 1e18;
    return sum;
}
signed main()
{
    scanf("%lld%lld", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%lld", x + i);
    for (int i = 1; i <= n; i++)
        scanf("%lld", y + i);
    for (int i = 1, a, b, z; i <= m; i++)
        scanf("%lld%lld%lld", &a, &b, &z),
            addE(a, b, z), e1[i] = e[i];
    int ans = kruskal();//情况1:两个超级节点都不在最小生成树里
    for (int i = 1; i <= n; i++)
        addE(n + 1, i, x[i]);
    ans = min(ans, kruskal());//情况2:超级节点1(飞机)在最小生成树里,超级节点2(轮船)不在最小生成树里
    for (int i = 1; i <= m; i++)
        e[i] = e1[i];
    cnt = m;
    for (int i = 1; i <= n; i++)
        addE(n + 2, i, y[i]);
    ans = min(ans, kruskal());//情况3:超级节点1(飞机)不在最小生成树里,超级节点2(轮船)在最小生成树里
    for (int i = 1; i <= n; i++)
        addE(n + 1, i, x[i]);
    ans = min(ans, kruskal());//情况1:两个超级节点都在最小生成树里
    printf("%lld\n", ans);
}

2.选择点

边化作点与点化作边不同,边化做点的要求十分苛刻:每一条边中,都是点 A A A与其他点相连,但数据很明显不是,因此无法直接实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值