【CF888G】Xor-MST

题目

题目链接:https://codeforces.com/problemset/problem/888/G
给定 \(n\) 个结点的无向完全图。每个点有一个点权为 \(a_i\)。连接 \(i\) 号结点和 \(j\) 号结点的边的边权为 \(a_i\text{ xor } a_j\)。求这个图的 MST 的权值。
\(1\le n\le 2\times 10^5\)\(0\le a_i< 2^{30}\)

思路

首先只考虑最高位,我们把最高位为 \(0\)\(1\) 的点分为两个集合,那么我们一定只会在这两个集合内连一条边。
推广到每一位,从高位到低位建出 Trie。然后考虑 Trie 上的一个节点 \(x\),我们一定只会在它的两个子树所表示集合之间连一条边。所以我们只需要求出这两个子树中哪两个数异或起来最小。
考虑枚举两个子树中数字更少的集合,在另一棵 Trie 中找到对应这个数字能异或到的最小数。这样每一个数字只会被枚举到 \(O(\log n)\) 次,时间复杂度 \(O(n\log^2 n)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=200010,LG=30,Inf=2147483647;
int n,ans;
ll sum;

struct Trie
{
	int tot,siz[N*LG],ch[N*LG][2];
	
	void ins(int x)
	{
		int p=0;
		for (int i=LG-1;i>=0;i--)
		{
			int id=(x>>i)&1;
			if (!ch[p][id]) ch[p][id]=++tot;
			p=ch[p][id]; siz[p]++;
		}
	}
	
	void dfs(int dep,int x,int y,int res)
	{
		if (!ch[x][0] && !ch[x][1])
			return ans=min(ans,res),void();
		for (int i=0;i<=1;i++)
			if (ch[x][i])
			{
				if (ch[y][i]) dfs(dep-1,ch[x][i],ch[y][i],res);
					else dfs(dep-1,ch[x][i],ch[y][i^1],res|(1<<dep-1));
			}
	}
	
	void solve(int dep,int x)
	{
		if (ch[x][0]) solve(dep-1,ch[x][0]);
		if (ch[x][1]) solve(dep-1,ch[x][1]);
		if (ch[x][0] && ch[x][1])
		{
			ans=Inf;
			if (siz[ch[x][0]]<siz[ch[x][1]])
				dfs(dep-1,ch[x][0],ch[x][1],(1<<dep-1));
			else
				dfs(dep-1,ch[x][1],ch[x][0],(1<<dep-1));
			sum+=ans;
		}
	}
}trie;

int main()
{
	scanf("%d",&n);
	for (int i=1,x;i<=n;i++)
	{
		scanf("%d",&x);
		trie.ins(x);
	}
	trie.solve(LG,0);
	printf("%lld",sum);
	return 0;
}
posted @ 2021-03-07 20:17  stoorz  阅读(170)  评论(0编辑  收藏  举报