Luogu CF1245D
题解 CF1245D
思路分析及算法分析
这道题的想法其他 dalao 也都讲了,就是套一个虚拟节点再向上建边的想法。
考虑到这道题每两个节点之间都会有边。
Kruscal 的复杂度就到了 \(O(K \times \log_2 K) = O(N^2 \times \log_2 N^2)\)。
而 prim 的复杂度还是稳定的 \(O(N^2)\)。
prim 要优化一个 \(log_2 N^2\) 时间,所以选择用 prim 解决这道题。
代码及讲解
# include <bits/stdc++.h>
using namespace std;
const int maxn = 2005;
struct reader {
template <typename Type>
reader & operator >> (register Type & ret) {
register int f = 1; ret = 0; register char ch = getchar ();
for (;!isdigit (ch); ch = getchar ()) if (ch == '-') f=-f;
for (; isdigit (ch); ch = getchar ()) ret = (ret << 1) + (ret << 3) + (ch - '0');
ret *= f; return *this;
}
} fin;
int N, bd; // bd指的是直接建立一个电源的节点个数
long long K[maxn];
long long X[maxn];
long long Y[maxn]; // 这些如题
long long las[maxn]; // 计入上一次更新这个点是哪个点,如果选上那就代表 las[i] 与 i 之间有边
long long res; // 最后的答案
long long dis[maxn]; // 当前树到各个非树节点的最小值
bool vis[maxn]; // 记录当前是否有被加入树中
int main () {
fin >> N;
for (register int i = 1; i <= N; i++) fin >> X[i] >> Y[i];
for (register int i = 1; i <= N; i++) fin >> dis[i];
for (register int i = 1; i <= N; i++) fin >> K[i], las[i] = 0;
//读入数据的同时讲上一次更新的设为0号节点,然后将 dis 设为直接建立电力站的代价,也就是普通prim将所有点设置为初始的0号点的权值
for (register int t = 1; t <= N; t++) {
register int minn = 2147483640, id;
for (register int i = 1; i <= N; i++)
if (!vis[i] && dis[i] < minn) minn = dis[i], id = i;
vis[id] = true; res += minn; if (las[id] == 0) bd++;
for (register int i = 1; i <= N; i++)
if (!vis[i] && dis[i] > (K[i] + K[id]) * (abs (X[id] - X[i]) + abs (Y[id] - Y[i])))
dis[i] = (K[i] + K[id]) * (abs (X[id] - X[i]) + abs (Y[id] - Y[i])), las[i] = id;
//更新点的同时建立记录此次是哪个点更新,这里上文有说
}
printf ("%lld\n%d\n", res, bd);
for (register int i = 1; i <= N; i++)
if (las[i] == 0) printf ("%d ", i);
printf ("\n%d\n", N - bd);
//如果直接设立电力站的代价是 bd 的话,那么剩下 N-bd 个点肯定都是连边的,一个点一条边
for (register int i = 1; i <= N; i++)
if (las[i] != 0) printf ("%lld %d\n", las[i], i);
return 0;
}
最后,将这句老生常谈的话再拿出来:十年OI一场空,不开longlong见祖宗
Made By \(\text{wheneveright}\).

浙公网安备 33010602011771号