[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;
}
posted @ 2020-02-07 21:35  小飞淙的云端  阅读(82)  评论(0编辑  收藏