[CF888G]Xor-MST【Trie】【分治】【启发式合并】

>Link

luogu CF888G

luogu T207971


>Description

有一张 n n n 个点的无向完全图。其中点 i i i 的点权为 a i a_i ai
两个点 u , v u,v u,v 之间的边的边权为 a u a_u au

求出这张图的最小生成树。


>解题思路

看到求类似异或的最小值,就会想到用Trie
求最小生成树,要找当前最小的两两异或值
发现在Trie树上,如果两两叶子节点的LCA深度越深,这两个点的异或值就越小,那这两个点就越优先连起来
所以我们在一个节点,找以这个节点为LCA要连起来的两个点,如果这个节点左右子树都非空,那这两个子树内部肯定优先连好了,即这两个子树已经是两个连通块
我们现在找一条价值最小的边把两棵子树连起来就行了。考虑启发式合并,枚举叶子节点数量更少的一棵子树,在另一棵子树的trie上匹配它,找到最小值,然后连起来。这其实就是一个分治的过程
时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

找一棵子树里面的所有叶子节点,可以用dfs序来实现,而且显而易见的是不管一棵子树内怎么合并,它们最终都是那几个点并在一起,大小不变 好sb我没思考,一开始还去写了个并查集并来并去,然后就出锅了QwQ


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
#define N 200010
#define inf 2147483648
using namespace std;

int n, cnt, dfn[N * 30], siz[N * 30], ed[N * 30];
int b[N], a[N], t[N * 30][3], tot, dep[N * 30];
LL ans, minn;

void getdfn (int now)
{
    
    
	if (!t[now][0] && !t[now][1])
	{
    
    
		siz[now] = 1;
		dfn[now] = ++cnt;
		a[dfn[now]] = ed[now];
		return;
	}
	if (t[now][0]) getdfn (t[now][0]);
	if (t[now][1]) getdfn (t[now][1]);
	siz[now] = siz[t[now][0]] + siz[t[now][1]];
	dfn[now] = min (dfn[t[now][0]], dfn[t[now][1]]);
	return;
}
void dfs (int now)
{
    
    
	if (!t[now][0] && !t[now][1]) return;
	if (!t[now][0]) dfs (t[now][1]);
	else if (!t[now][1]) dfs (t[now][0]);
	else
	{
    
    
		dfs (t[now][0]), dfs (t[now][1]);
		int k = 0;
		if (siz[t[now][0]] > siz[t[now][1]]) k = 1;
		int x, pos;
		minn = inf;
		for (int i = dfn[t[now][k]]; i <= dfn[t[now][k]] + siz[t[now][k]] - 1; i++)
		{
    
    
			pos = t[now][k ^ 1];
			for (int j = dep[pos]; j >= 0; j--)
			{
    
    
				x = (a[i] >> j) & 1;
				if (t[pos][x]) pos = t[pos][x];
				else pos = t[pos][x ^ 1];
			}
			minn = min (minn, (LL)a[i] ^ (LL)a[dfn[pos]]);
		}
		ans += minn;
	}
}

int main()
{
    
    
	memset (dfn, 0x7f, sizeof (dfn));
	int now, x;
	scanf ("%d", &n);
	dep[0] = 30;
	for (int i = 1; i <= n; i++)
	{
    
    
		scanf ("%d", &b[i]);
		now = 0;
		for (int j = 30; j >= 0; j--)
		{
    
    
			x = (b[i] >> j) & 1;
			if (!t[now][x]) t[now][x] = ++tot, dep[tot] = j - 1;
			now = t[now][x];
		}
		ed[now] = b[i];
	}
	getdfn (0);
	dfs (0);
	printf ("%lld", ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43010386/article/details/121237745