题目大意:给你$n(nleqslant2 imes10^5)$个点和$m(mleqslant2 imes10^5)$条边,第$i$个点点权为$a_i$。连接$u,v$两个点的代价为$a_u+a_v$或者一条连接$u,v$的边的边权。问连通的最小代价
题解:发现若不考虑特殊边,一定是点权最小的点连向其他点。于是建出由点权最小的点连向其他各点的边,边权为两点点权和。与特殊边一起跑最小生成树即可。
卡点:无
C++ Code:
#include <algorithm> #include <cstdio> #define maxn 200010 int n, m; int l[maxn << 1], r[maxn << 1], rnk[maxn << 1]; long long ans, a[maxn], w[maxn << 1]; int f[maxn]; int find(int x) { return x == f[x] ? x : (f[x] = find(f[x])); } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) { scanf("%lld", a + i); rnk[i] = f[i] = i; } std::sort(rnk + 1, rnk + n + 1, [] (int x, int y) { return a[x] < a[y]; }); const long long base = a[rnk[1]]; const int L = rnk[1]; for (int i = 1; i < n; ++i) { w[i] = a[rnk[i + 1]] + base; l[i] = L, r[i] = rnk[i + 1]; rnk[i] = i; } for (int i = n; i < n + m; ++i) { scanf("%d%d%lld", l + i, r + i, w + i); rnk[i] = i; } std::sort(rnk + 1, rnk + n + m, [] (int x, int y) { return w[x] < w[y]; }); int num = n - 1; for (int i = 1, u, v; i < n + m && num; ++i) { u = find(l[rnk[i]]), v = find(r[rnk[i]]); if (u != v) { f[u] = v; ans += w[rnk[i]]; --num; } } printf("%lld ", ans); return 0; }