[CF888G]Xor-MST
G - Xor-MST
题解
最小异或生成树
先把所有二进制数上Trie树,然后为了使得异或最小,根据异或的性质,trie树的左右子树都应该自己先连成一个连通块,然后在两个连通块内找到两个数使得异或值最小,这样就能进行连通了。
左右子树的话递归下去处理,在计算两个子树上异或值最小的时候用启发式合并,枚举大小较小的那棵子树的数值,在另外一棵子树上跑。
复杂度\(O(nloglogn)\)
为什么我维护了Trie树的那么多信息??
#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int maxn=200010;
int n,v[maxn],ch[32*maxn][2],size[32*maxn],sz,val[maxn*32],L[maxn*32],R[maxn*32],dfn[32*maxn],deep[32*maxn],dfs_clock=1;
void insert(int x)
{
int now=0;
for(int i=31;i>=0;i--)
{
int c=(x>>i)&1;
if(!ch[now][c])ch[now][c]=++sz;
now=ch[now][c];
}
val[now]=x;
}
void dfs(int now)
{
L[now]=dfs_clock;
if(val[now])dfn[dfs_clock++]=val[now],size[now]=1;
for(int i=0;i<2;i++)
if(ch[now][i])
{
deep[ch[now][i]]=deep[now]-1;
dfs(ch[now][i]);
size[now]+=size[ch[now][i]];
}
R[now]=dfs_clock;
}
int query(int x,int u)
{
x&=((1<<deep[u])-1);
int now=u;
for(int i=deep[u]-1;i>=0;i--)
{
int c=(x>>i)&1;
if(!ch[now][c])c^=1;
now=ch[now][c];
}
return val[now];
}
LL calc(int u)
{
if(!ch[u][0] && !ch[u][1])return 0;
if(!ch[u][0])return calc(ch[u][1]);
if(!ch[u][1])return calc(ch[u][0]);
int x=ch[u][0],y=ch[u][1],ans=2147483647;
if(size[x]>size[y])swap(x,y);
for(int i=L[x];i<R[x];i++)ans=min(ans,query(dfn[i],y)^dfn[i]);
return calc(ch[u][0])+calc(ch[u][1])+ans;
}
int main()
{
n=read();
for(int i=0;i<n;i++)v[i]=read();
sort(v,v+n);
n=unique(v,v+n)-v;
for(int i=0;i<n;i++)insert(v[i]);
deep[0]=32;dfs(0);
printf("%lld\n",calc(0));
return 0;
}