CF2081D. MST in Modulo Graph

MST in Modulo Graph

思维怎么练?

题意

给定一个包含 \(n\) 个顶点的完全图, 其中第 \(i\) 个顶点的权重为 \(p_i\). 连接顶点 \(x\) 和顶点 \(y\) 的边的权重等于 \(\operatorname{max}(p_x, p_y) \bmod \operatorname{min}(p_x, p_y)\).

请找到一组 \(n - 1\) 条边, 使得这组边能够连接所有 \(n\) 个顶点, 并且这组边的总权重最小.

思路

由于边数是 \(\mathcal{O}(n^2)\) 级别的, 我们不能直接进行连边跑 Kruskal. 不难发现, 其实在图中很多边是不必要或者一定不优的, 我们考虑从减少连边的角度入手.

首先, 对于权值相同的节点, 我们可以将它们全部缩成一个点 \((\)因为两两连边代价是 0, 不会产生贡献\()\) 再来考虑. 假设当前枚举的点权值为 \(x\), 那么与它相连的边的权值一定在 \(0 \sim x - 1\) 的范围内, 我们考虑将其他点分成几个区间: \([x, 2x], [2x, 3x], \dots, [(k - 1)x, kx]\), 其中 \(\displaystyle k = \lfloor \frac{\max p_i}{x} \rfloor\). 对于每一个区间, 我们只向点权最小的那一个点连边. 证明一下, 如果存在 \(x < y < z < 2x\), 其中 \(y, z\) 均指点权, 那么 \(x \to y, y \to z\) 一定比 \(x \to y, x \to z\) 优, 至于 \(y \to z\) 的边, 我们会在枚举 \(y\) 时操作.

这样, 边的数量减少到的 \(\mathcal{O}(n \log n)\) 级别. 再跑一个 Kruskal, 总复杂度 \(\mathcal{O}(n \log^2 n)\).

void init() {
	e.clear();
	read(n);
	for (int i = 1; i <= n; ++i) {
		read(p[i]);
	}
	sort(p + 1, p + n + 1);
	n = unique(p + 1, p + n + 1) - p - 1;
	iota(fa, fa + n + 1, 0);
}

void calculate() {
	for (int i = 1; i <= n; ++i) {
		for (int _ = 1, lim = p[n] / p[i]; _ <= lim; ++_) {
			int k = _ * p[i], pos = lower_bound(p + 1, p + n + 1, k) - p;
			if (pos == i) {
				pos = upper_bound(p + 1, p + n + 1, k) - p;
			}
			if (pos > n) {
				continue;
			}
			e.push_back({i, pos, p[pos] % p[i]});
		}
	}
	ranges::sort(e, [](Edge x, Edge y) {
		return x.w < y.w;
	});
	int cnt = 0;
	ll ans = 0;
	for (auto [u, v, w] : e) {
		if (cnt == n - 1) {
			break;
		}
		u = find(u), v = find(v);
		if (u ^ v) {
			++cnt;
			ans += w;
			fa[u] = v;
		}
	}
	printf("%lld\n", ans);
}
posted @ 2025-03-17 11:59  Steven1013  阅读(49)  评论(0)    收藏  举报