CF888G Xor-MST DFS 最小生成树 01Trie

Xor-MST

传送门

题面翻译

  • 给定 n n n 个结点的无向完全图。每个点有一个点权为 a i a_i ai。连接 i i i 号结点和 j j j 号结点的边的边权为 a i ⊕ a j a_i\oplus a_j aiaj
  • 求这个图的 MST 的权值。
  • 1 ≤ n ≤ 2 × 1 0 5 1\le n\le 2\times 10^5 1n2×105 0 ≤ a i < 2 30 0\le a_i< 2^{30} 0ai<230

题目描述

You are given a complete undirected graph with n n n vertices. A number a i a_{i} ai is assigned to each vertex, and the weight of an edge between vertices i i i and j j j is equal to a i x o r a j a_{i}xora_{j} aixoraj .

Calculate the weight of the minimum spanning tree in this graph.

输入格式

The first line contains n n n ( 1 < = n < = 200000 1<=n<=200000 1<=n<=200000 ) — the number of vertices in the graph.

The second line contains n n n integers a 1 a_{1} a1 , a 2 a_{2} a2 , …, a n a_{n} an ( 0 < = a i < 2 30 0<=a_{i}<2^{30} 0<=ai<230 ) — the numbers assigned to the vertices.

输出格式

Print one number — the weight of the minimum spanning tree in the graph.

样例 #1

样例输入 #1

5
1 2 3 4 5

样例输出 #1

8

样例 #2

样例输入 #2

4
1 2 3 4

样例输出 #2

8

以上来自洛谷 以上来自洛谷 以上来自洛谷

解题思路

先讲 B o r u v k a Boruvka Boruvka 算法:当前每个集合伸出去一条最短的边,然后把联通块缩成一个新的集合,用这种思想处理 M S T MST MST。(学习 M S T MST MST这里
但是如果我们要使得异或和最小,这个很容易想到 01 T r i e 01Trie 01Trie(学习 T r i e Trie Trie 树看这里),考虑将每一个点按照权值插到 T r i e Trie Trie 中,这样我们考虑叶子节点就是实际存在的点,而非叶子节点一定时若干对叶子节点的 L C A LCA LCA。(不知道 L C A LCA LCA?看这里
根据二进制的性质,不难发现我们先合并 L C A LCA LCA 深度大的点对更优,这样我们考虑 dfs ⁡ \operatorname{dfs} dfs 整个 T r i e Trie Trie,对于一个非叶子节点,我们枚举他的 0 / 1 0/1 0/1 子树的所有儿子,然后在 1 / 0 1/0 1/0 子树中找到边权最小的来合并,如果我们从小到大插入,那么编号就是连续的,然后启发式合并即可。

AC Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int Maxn = 6e6 + 5;
int n, a[Maxn];
int rt, L[Maxn], R[Maxn], ch[Maxn][2], tot;
inline void Insert(int &now, int x, int dep) {
	if (!now) {
		now = ++tot;
	}
	L[now] = min(L[now], x), R[now] = max(R[now], x);
	if (dep < 0) {
		return;
	}
	int bit = a[x] >> dep & 1;
	Insert(ch[now][bit], x, dep - 1);
}
inline int query(int now, int val, int dep) {
	if (dep < 0) {
		return 0;
	}
	int bit = val >> dep & 1;
	if (ch[now][bit]) {
		return query(ch[now][bit], val, dep - 1);
	} else {
		return query(ch[now][bit ^ 1], val, dep - 1) + (1 << dep);
	}
}
inline int dfs(int now, int dep) {
	if (dep < 0) {
		return 0;
	}
	if (R[ch[now][0]] && R[ch[now][1]]) {
		int minn = INT_MAX;
		for (int i = L[ch[now][0]]; i <= R[ch[now][0]]; i++) {
			minn = min(minn, query(ch[now][1], a[i], dep - 1));
		}
		return dfs(ch[now][0], dep - 1) + dfs(ch[now][1], dep - 1) + minn + (1 << dep);
	}
	if (R[ch[now][0]]) {
		return dfs(ch[now][0], dep - 1);
	}
	if (R[ch[now][1]]) {
		return dfs(ch[now][1], dep - 1);
	}
	return 0;
}
inline void work() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	sort(a + 1, a + n + 1);
	memset(L, 0x3f, sizeof(L));
	for (int i = 1; i <= n; i++) {
		Insert(rt, i, 30);
	}
	cout << dfs(rt, 30) << endl;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	work();
	return 0;
}
  • 27
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值