CF1245D Shichikuji and Power Grid 题解:超级源点的使用

· · 题解

题意:

已知一个平面上有 n 个城市,需要个 n 个城市均通上电。
一个城市有电,必须在这个城市有发电站或者和一个有电的城市用电缆相连。

在一个城市建造发电站的代价是 c[i],将 ij 两个城市用电缆相连的代价是 k[i]+k[j] 乘上两者的曼哈顿距离。

求最小代价的方案。

思路

首先题目要求连通所有 n 个城市,很容易想到这是最小生成树。连通方式有:

我们设想一个有很多电的新城市,发现在城市中建发电站其实相当于连接到这个新城市。这就是本题的思路:超级源点。

我们将建发电站全部转化为连向序号为 0 的超级源点,每次建发电站就建立一条 0 连向该点的边,边权可以通过结构体构造函数直接计算。而其他边则每两条边连边即可,本题 n 的数据范围不超过 2000,则不会 \text{TLE}。那么我们接下来只需要跑一边 \text{Kruskal} 就可以 AC 本题了。

需要注意:

预处理时间复杂度 $\mathcal{O}(n^2)$,上限在于为每两座城市建边。 $\text{Kruskal}$ 时间复杂度 $\mathcal{O}(n^2 \log n^2)$。 ## Code ```cpp #include <bits/stdc++.h> using namespace std; #define int long long int n,mst,cnt_to_zero,cnt_line; int k[2010],c[2010]; struct Node { int x,y; Node() {} Node(int a,int b) : x(a),y(b) {} // 构造函数 } node[2010]; int Manhatton(Node a,Node b) { // 曼哈顿距离 return abs(a.x - b.x) + abs(a.y - b.y); } struct Edge { int u,v,w; Edge() {} Edge(int a,int b) : u(a),v(b) { // 构造函数 if (u == 0) { // 如果向超级源点连边 w = c[v]; // 边权即建发电站的代价 } else { w = (k[u] + k[v]) * Manhatton(node[u],node[v]); // 按照题目条件计算 } } }; vector<Edge> edge; vector<int> city_to_zero; vector<Node> line; class DSU { // 并查集 int fa[2010]; public : void init(int size) { for (int i=0; i<=size; ++i) { fa[i] = i; } } int find(int x) { if (x != fa[x]) { fa[x] = find(fa[x]); } return fa[x]; } void unite(int x,int y) { fa[find(x)] = find(y); } } dsu; int Kruskal() { sort(edge.begin(),edge.end(),[](Edge a,Edge b) { return a.w < b.w; }); for (auto ver : edge) { if (dsu.find(ver.u) != dsu.find(ver.v)) { dsu.unite(ver.u,ver.v); mst += ver.w; if (ver.u == 0) { ++cnt_to_zero; city_to_zero.push_back(ver.v == 0 ? ver.u : ver.v); } else { ++cnt_line; line.push_back({ver.u,ver.v}); } } } return mst; } signed main() { scanf("%lld",&n); dsu.init(n); for (int i=1; i<=n; ++i) { scanf("%lld %lld",&node[i].x,&node[i].y); } for (int i=1; i<=n; ++i) { scanf("%lld",&c[i]); } for (int i=1; i<=n; ++i) { scanf("%lld",&k[i]); } for (int i=1; i<=n; ++i) { edge.push_back({0,i}); // Kruskal里面无向边加一次就够了 for (int j=1; j<=n; ++j) { edge.push_back({i,j}); } } printf("%lld\n",Kruskal()); printf("%lld\n",cnt_to_zero); for (auto city : city_to_zero) { printf("%lld ",city); } printf("\n%lld\n",cnt_line); for (auto cityline : line) { printf("%lld %lld\n",cityline.x,cityline.y); } return 0; } ```