标签:++ com 字典 xor tmp scan 题目 include lse
给出一个 \(n\) 个点的无向完全图,每个点的点权为:\(a_i\),每条边的权值为该边两个端点的点权的异或值。求出这个图最小生成树的权值。
\(1\leq n \leq 200000,0\leq a_i < 2^{30}\)
题目链接:https://codeforces.com/problemset/problem/888/G
最小异或生成树,把点权存储在字典树上进行匹配。
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int N=2e5+5;
const int maxn=3e6+5;
int a[N],trie[maxn][2],cnt;
int id[maxn];
vector<int>value[N];
ll ans;
void add(int x,int k)
{
int rt=1;
for(int i=29;i>=0;i--)
{
int t=((x>>i)&1);
if(trie[rt][t]==0)
trie[rt][t]=++cnt;
rt=trie[rt][t];
}
id[rt]=k;
value[k].pb(x);
}
int matching(int x,int rt,int d)
{
int res=(1<<d);
for(int i=d-1;i>=0;i--)
{
int t=((x>>i)&1);
if(trie[rt][t]>0)
rt=trie[rt][t];
else
{
rt=trie[rt][1-t];
res|=(1<<i);
}
}
return res;
}
void solve(int rt,int d)//注意d的取值,字典树以边表示二进制位的值
{
if(trie[rt][0]>0) solve(trie[rt][0],d-1);
if(trie[rt][1]>0) solve(trie[rt][1],d-1);
if(trie[rt][0]>0&&trie[rt][1]>0)//分叉点
{
int x=id[trie[rt][0]],y=id[trie[rt][1]];
int min_xor=(1<<30);
if(value[x].size()<value[y].size())//选取小的子树
{
for(int i=0;i<value[x].size();i++)
{
int tmp=value[x][i];
int xr=matching(tmp,trie[rt][1],d-1);
min_xor=min(xr,min_xor);
value[y].pb(tmp);
}
id[rt]=y;
}
else
{
for(int i=0;i<value[y].size();i++)
{
int tmp=value[y][i];
int xr=matching(tmp,trie[rt][0],d-1);
min_xor=min(xr,min_xor);
value[x].pb(tmp);
}
id[rt]=x;
}
ans+=min_xor;
}
else
{//上传到父亲节点
if(trie[rt][0]>0||trie[rt][1]>0)
id[rt]=id[trie[rt][0]+trie[rt][1]];
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+1+n);
cnt=1;
add(a[1],1);
for(int i=2;i<=n;i++)
{
if(a[i]!=a[i-1])
add(a[i],i);
}
ans=0;
solve(1,30);
printf("%lld\n",ans);
return 0;
}
标签:++ com 字典 xor tmp scan 题目 include lse
原文地址:https://www.cnblogs.com/1024-xzx/p/13644296.html