【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;
}