[解题报告] CF888G Xor-MST (Trie 树 + 异或)

传送🚪

题意

给定一个 \(n\) 个节点的无向完全图, 点 \(i\) 的权值为 \(a_i\), 边 \((i,j)\) 的权值为 \(a_i \oplus a_j\).

求该图的最小生成树的边权之和.

\(1 \le n \le 2 \times 10^5,\ 0 \le a_i < 2^{30}\).


思路

开始根据 Prim 算法想了一个奇奇怪怪的线段树, 写了一个多小时发现是假的......

然后就滚去看题解了.


求最小生成树的过程中, 我们要使得合并的两个点的异或值尽量小, 那么就要求两个数按二进制位从大到小相同的位数尽量的多, 我们可以考虑按 \(a_i\) 的二进制位建一棵 Trie 树, 然后在 Trie 树上贪心.

显然, 在 Tire 树上, 一定是按深度从大到小合并两棵子树最优 (因为这样它们的前面几位异或起来都为 0).

在合并两棵子树时, 我们把 size 较小的那棵树的所有节点拿出来, 然后在另一棵子树上贪心地走, 最后对较小子树的所有节点的结果取一个最小值就好了.

考虑一下时间复杂度. 因为每次我们都是将较小的那棵子树拿出来, 所以每一层最多拿出来 \(\frac{n}{2}\) 个点 (当 Trie树是完全二叉树时). 总共有 \(\log a\) 层, 每个点贪心的复杂度是 \(O(\log a)\) 的, 所以总时间复杂度为 \(O(n \log^2 a)\).


代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int _ = 1e5 + 7;
const int __ = 5e6 + 7;
const int L = 29;

struct tire {
	int ls[__], rs[__], sz[__], rt, cnt, minx;

	void insert(int &k, int t, int x) {
		if (!k) k = ++cnt;
		++sz[k];
		if (t < 0) return;
		if (x >> t & 1) insert(rs[k], t - 1, x);
		else insert(ls[k], t - 1, x);
	}

	void ins(int x) { insert(rt, L, x); }

	void merge(int k1, int k2, int t, int sum) {
		if (sum > minx) return;
		if (t < 0) { minx = min(minx, sum); return; }
		if (ls[k1]) {
			if (ls[k2]) merge(ls[k1], ls[k2], t - 1, sum);
			else merge(ls[k1], rs[k2], t - 1, sum + (1 << t));
		}
		if (rs[k1]) {
			if (rs[k2]) merge(rs[k1], rs[k2], t - 1, sum);
			else merge(rs[k1], ls[k2], t - 1, sum + (1 << t));
		}
	}

	ll qu(int k, int t) {
		if (!k || t < 0) return 0;
		ll t1 = qu(ls[k], t - 1);
		ll t2 = qu(rs[k], t - 1);
		if (ls[k] * rs[k] == 0) return t1 + t2;
		else {
			minx = 0x3f3f3f3f;
			if (sz[ls[k]] < sz[rs[k]]) merge(ls[k], rs[k], t - 1, 0);
			else merge(rs[k], ls[k], t - 1, 0);
			return t1 + t2 + minx + (1 << t);
		}
	}

	ll query() { return qu(rt, L); }
}T;

int n, a;

int main () {
#ifndef ONLINE_JUDGE
	freopen("x.in", "r", stdin);
#endif
	cin >> n;
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a);
		T.ins(a);
	}
	printf("%lld\n", T.query());
	return 0;
}
posted @ 2020-07-31 14:24  BruceW  阅读(119)  评论(0编辑  收藏  举报