LL getdis(int a, int b){ if (a > b) swap(a, b); if (a == 0) return c[b]; return (LL)(k[a] + k[b]) * (abs(x[a] - x[b]) + abs(y[a] - y[b])); }
voidkruskal(){ int m = 0; for (int i = 1; i <= n; i++) { edg[m++] = {0, i, getdis(0, i)}; } for (int i = 1; i <= n; i++) { for (int j = i+1; j <= n; j++) { edg[m++] = {i, j, getdis(i, j)}; } } sort(edg, edg+m); for (int i = 1; i <= n; i++) p[i] = i; vector<PII> ans; LL cost = 0; for (int i = 0; i < m; i++) { int a = find(edg[i].a), b = find(edg[i].b); if (a != b) { p[a] = b; cost += edg[i].w; ans.push_back({edg[i].a, edg[i].b}); } } sort(ans.begin(), ans.end()); int cnt = 0; for (const PII& p: ans) { if (p.first == 0) cnt++; } printf("%lld\n%d\n", cost, cnt); for (int i = 0; i < cnt; i++) { printf("%d ", ans[i].second); } printf("\n%d\n", ans.size() - cnt); for (int i = cnt; i < ans.size(); i++) { printf("%d %d\n", ans[i].first, ans[i].second); } }
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d%d", &x[i], &y[i]); for (int i = 1; i <= n; i++) scanf("%d", &c[i]); for (int i = 1; i <= n; i++) scanf("%d", &k[i]); returnkruskal(), 0; }
intmain(){ cin >> n >> k; for (int i = 1; i <= n; i++) { p[i] = i; cin >> q[i].x >> q[i].y; for (int j = 1; j < i; j++) { e[m++] = {i, j, q[j].dist(q[i])}; } } sort(e, e+m); int cnt = 0; double res = 0; for (int i = 0; i < m && cnt < n-k; i++) { int a = find(e[i].a), b = find(e[i].b); if (a != b) { p[a] = b; cnt++; res = e[i].w; } } printf("%.2lf\n", res); return0; }
最小生成树的边权和最小完全图
给定一棵 N 个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。
intmain(){ int T; cin >> T; while (T--) { cin >> n; for (int i = 1; i <= n; i++) p[i] = i, sz[i] = 1; for (int i = 0; i < n-1; i++) { cin >> e[i].a >> e[i].b >> e[i].w; } sort(e, e+n-1); int res = 0; for (int i = 0; i < n-1; i++) { int a = find(e[i].a), b = find(e[i].b); if (a != b) { p[a] = b; res += (sz[b]*sz[a]-1)*(e[i].w+1); sz[b] += sz[a]; } } cout << res << endl; } return0; }
// 求从 u 出发路径上的最大值和次大值 voiddfs(int u, int par, int maxd1, int maxd2, int dist1[], int dist2[]){ dist1[u] = maxd1; dist2[u] = maxd2; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (j == par) continue; // 这个地方不要直接改 maxd1 maxd2 因为下一轮循环要用原始数据 int t1 = maxd1, t2 = maxd2; if (w[i] > t1) t2 = t1, t1 = w[i]; elseif (t2 < w[i] && w[i] < t1) t2 = w[i]; dfs(j, u, t1, t2, dist1, dist2); } }
intmain(){ cin >> n >> m; for (int i = 1; i <= n; i++) p[i] = i; memset(h, -1, sizeof h); for (int i = 0; i < m; i++) { int a, b, w; cin >> a >> b >> w; edges[i] = {a, b, w}; } sort(edges, edges+m); // 最多 500 个结点 每条边的范围是 1~1e9 所以会爆 int ll S = 0; for (int i = 0; i < m; i++) { int a = edges[i].a, b = edges[i].b, w = edges[i].w; int pa = find(a), pb = find(b); if (pa != pb) { p[pa] = pb; S += w; edges[i].f = true; add(a, b, w), add(b, a, w); } } // 默认最大距离设成 -1e9 要不然不知道是真的 0 还是默认为 0 for (int i = 1; i <= n; i++) dfs(i, -1, -1e9, -1e9, dist1[i], dist2[i]); // 枚举非树边 ll res = 1e18; for (int i = 0; i < m; i++) { if (edges[i].f) continue; int a = edges[i].a, b = edges[i].b, w = edges[i].w; // S - wt + w > S if (w > dist1[a][b]) { res = min(res, S-dist1[a][b]+w); } elseif (w > dist2[a][b]) { res = min(res, S-dist2[a][b]+w); } } cout << res << endl; return0; }
for (auto& ed: edge) { int a = ed.a, b = ed.b, w = ed.w; if (find(a) != find(b)) { p[find(a)] = find(b); add(a, b, w), add(b, a, w); ed.used = true; } } }
voidinitdfs(int u, int father, int weight){ fa[u][0] = father; d[u][0] = weight; dep[u] = dep[father] + 1; for (int k = 1; k < K; k++) { int v = fa[u][k-1]; fa[u][k] = fa[v][k-1]; d[u][k] = max(d[u][k-1], d[v][k-1]); }
for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (j == father) continue; initdfs(j, u, w[i]); } }
voidmodify(int a, int b, int w){ if (dep[a] < dep[b]) swap(a, b); for (int k = K-1; k >= 0; k--) { if (dep[fa[a][k]] >= dep[b]) { path[a][k] = min(path[a][k], w); a = fa[a][k]; } } if (a == b) return; for (int k = K-1; k >= 0; k--) { if (fa[a][k] != fa[b][k]) { path[a][k] = min(path[a][k], w); path[b][k] = min(path[b][k], w); a = fa[a][k], b = fa[b][k]; } } path[a][0] = min(path[a][0], w); path[b][0] = min(path[b][0], w); }
intgetmaxd(int a, int b){ if (dep[a] < dep[b]) swap(a, b);
int res = 0; for (int k = K-1; k >= 0; k--) { if (dep[fa[a][k]] >= dep[b]) { res = max(res, d[a][k]); a = fa[a][k]; } } if (a == b) return res;
for (int k = K-1; k >= 0; k--) { if (fa[a][k] != fa[b][k]) { res = max(res, max(d[a][k], d[b][k])); a = fa[a][k], b = fa[b][k]; } } returnmax(res, max(d[a][0], d[b][0])); }
voidupdate(){ for (auto& ed: edge) { if (ed.used) continue; modify(ed.a, ed.b, ed.w); }
vector<int> nums(n); iota(nums.begin(), nums.end(), 1); sort(nums.begin(), nums.end(), [](int a, int b){ return dep[a] > dep[b]; }); for (int u: nums) { for (int k = K-1; k; k--) { int v = fa[u][k-1]; // path[u][k] -> path[u][k-1], path[v][k-1] path[u][k-1] = min(path[u][k-1], path[u][k]); path[v][k-1] = min(path[v][k-1], path[u][k]); } } }
voidmerge(int a, int b){ p[find(a)] = find(b); } } dsu;
structGraph2 { structEdge { int to, nxt, w, id; } e[N<<1]; int h[N], dep[N], fa[N][K], d[N][K], idx = 2;
voidadd(int a, int b, int c, int id){ e[idx] = {b, h[a], c, id}, h[a] = idx++; }
voiddfs(int u, int f, int from){ fa[u][0] = f, d[u][0] = from, dep[u] = dep[f] + 1; for (int k = 1; k < K; k++) { fa[u][k] = fa[fa[u][k-1]][k-1]; d[u][k] = max(d[u][k-1], d[fa[u][k-1]][k-1]); } for (int i = h[u]; i; i = e[i].nxt) { int to = e[i].to, w = e[i].w; if (to == f) continue; dfs(to, u, w); } }
intmaxd(int a, int b){ int res = 0; if (dep[a] < dep[b]) swap(a, b); for (int k = K-1; k >= 0; k--) if (dep[fa[a][k]] >= dep[b]) res = max(res, d[a][k]), a = fa[a][k]; if (a == b) return res; for (int k = K-1; k >= 0; k--) if (fa[a][k] != fa[b][k]) res = max(res, max(d[a][k], d[b][k])), a = fa[a][k], b = fa[b][k]; returnmax(res, max(d[a][0], d[b][0])); } } G2;
structKruskal { structEdge { int a, b, c, id; bool used; booloperator<(const Edge& ed) const { return c < ed.c; } } e[N]; bool ans[N]; int idx;
voidadd(int a, int b, int c, int id){ e[++idx] = {a, b, c, id}; }
longlongbuild(){ longlong res = 0; sort(e+1, e+1+idx); dsu.init(n);
for (int i = 1; i <= idx; i++) { int a = e[i].a, b = e[i].b, c = e[i].c, id = e[i].id; if (dsu.find(a) == dsu.find(b)) continue; dsu.merge(a, b); G2.add(a, b, c, id), G2.add(b, a, c, id); e[i].used = true; res += c; }
return res; }
intsolve(){ int tot = 0; G2.dfs(1, 0, 0); for (int i = 1; i <= idx; i++) { if (e[i].used || G2.maxd(e[i].a, e[i].b) == e[i].c) { ans[e[i].id] = true; tot++; } } return tot; } } krus;
voidadd(int a, int b){ e[idx] = {b, h[a]}, h[a] = idx++; }
voiddfs(int u, int f){ leaf[u] = true; l[u] = INF, r[u] = 0; for (int i = h[u]; i; i = e[i].nxt) { int to = e[i].to; if (to == f) continue; dfs(to, u); leaf[u] = false; l[u] = min(l[u], l[to]); r[u] = max(r[u], r[to]); }
if (leaf[u]) l[u] = r[u] = dfn[u] = ++ts; }
voidbuild(){ for (int i = 1; i <= n; i++) { krus.add(l[i], r[i]+1, w[i], i); } } } G1;
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &G1.w[i]); for (int i = 1, a, b; i < n; i++) scanf("%d%d", &a, &b), G1.add(a, b), G1.add(b, a); G1.dfs(1, 0); G1.build(); longlong res = krus.build(); int tot = krus.solve(); printf("%lld %d\n", res, tot); for (int i = 1; i <= n; i++) if (krus.ans[i]) printf("%d ", i); return0; }
[CF773F] Drivers Dissatisfaction
给出一张 n 个点 m 条边的无向图,每条边 (ai,bi) 有一个权值 wi 和费用 ci,表示这条边 每降低 1 的权值需要 ci 的花费。现在一共有 S 费用可以用来降低某些边的权值(可以降到 负数),求图中的一棵权值和最小的生成树并输出方案。
#define int long long constint N = 200010, K = 20; int n, m, s, h[N], dep[N], fa[N][K], d[N][K], mx[N][K], idx = 2; structEdge { int to, nxt, w, id; } e[N<<1];
voidadd(int a, int b, int c, int id){ e[idx] = {b, h[a], c, id}, h[a] = idx++; }
structKrusEdge { int a, b, c, d, id; bool used; booloperator<(const KrusEdge& ed) const { return c < ed.c; } } edge[N];
structDSU { int p[N];
voidinit(int n){ for (int i = 1; i <= n; i++) p[i] = i; }
int sum = 0; for (int i = 1; i <= m; i++) { int a = edge[i].a, b = edge[i].b, c = edge[i].c, id = edge[i].id; if (dsu.find(a) == dsu.find(b)) continue; dsu.merge(a, b); edge[i].used = true; add(a, b, c, id), add(b, a, c, id); sum += edge[i].c; } return sum; }
voiddfs(int u, int f, int fromw, int fromid){ fa[u][0] = f, d[u][0] = fromw, mx[u][0] = fromid, dep[u] = dep[f] + 1; for (int k = 1; k < K; k++) { fa[u][k] = fa[fa[u][k-1]][k-1]; d[u][k] = max(d[u][k-1], d[fa[u][k-1]][k-1]); if (d[u][k-1] > d[fa[u][k-1]][k-1]) { d[u][k] = d[u][k-1]; mx[u][k] = mx[u][k-1]; } else { d[u][k] = d[fa[u][k-1]][k-1]; mx[u][k] = mx[fa[u][k-1]][k-1]; } }
for (int i = h[u]; i; i = e[i].nxt) { int to = e[i].to; if (to == f) continue; dfs(to, u, e[i].w, e[i].id); } }
// return id voidmaxd(int a, int b, int& id, int& val){ val = 0, id = 0; if (dep[a] < dep[b]) swap(a, b); for (int k = K-1; k >= 0; k--) if (dep[fa[a][k]] >= dep[b]) { if (d[a][k] > val) val = d[a][k], id = mx[a][k]; a = fa[a][k]; }
if (a == b) return;
for (int k = K-1; k >= 0; k--) if (fa[a][k] != fa[b][k]) { if (d[a][k] > val) val = d[a][k], id = mx[a][k]; if (d[b][k] > val) val = d[b][k], id = mx[b][k]; a = fa[a][k], b = fa[b][k]; }
if (d[a][0] > val) val = d[a][0], id = mx[a][0]; if (d[b][0] > val) val = d[b][0], id = mx[b][0]; }
voidread(int& t){ t = 0; char ch = getchar(); while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) t = t*10 + (ch^48), ch = getchar(); }
signedmain(){ read(n), read(m); for (int i = 1; i <= m; i++) edge[i].id = i, read(edge[i].c); for (int i = 1; i <= m; i++) read(edge[i].d); for (int i = 1; i <= m; i++) read(edge[i].a), read(edge[i].b); read(s);
int raw = kruskal(); dfs(1, 0, 0, 0);
Ans ans; ans.delta = 0x3f3f3f3f; for (int i = 1; i <= m; i++) { int delta = -s/edge[i].d, rm = 0; if (!edge[i].used) { int val = 0; maxd(edge[i].a, edge[i].b, rm, val); delta += edge[i].c - val; } ans = min(ans, Ans{delta, rm, edge[i].id}); }
printf("%lld\n", raw + ans.delta);
for (int i = 1; i <= m; i++) { int id = edge[i].id, w = edge[i].c; if (id == ans.rm) continue; if (edge[i].used || id == ans.affect) { if (id == ans.affect) w -= s / edge[i].d; printf("%lld %lld\n", id, w); } } return0; }