【Borůvka】【CF888G】

题目

【Borůvka】【CF888G】_第1张图片

思路

考虑Boruvka算法,我们相当于每次要查询联通块外面的最小边。每次迭代,我们将所有数插入trie,对于每个联通块,从trie中删除所有数,接下来对于每个数查询trie上异或和的最小值并记录,然后插入回trie。
这两个算法复杂度均为两个log。

代码

#include
#define ll long long
using namespace std;
vector<ll>Q[4000001];
int ch[4000001][2],pos,dep[4000001],n;
ll pw[31],ans,a[200001];
void insert(ll x)
{
     
	int now=0;
	Q[now].push_back(x);
	for(int i=30; i>=0; i--)
	{
     
		int flag=(bool)((x>>i)&1);
		if(ch[now][flag]==0) ch[now][flag]=++pos;
		now=ch[now][flag];
		x-=flag*pw[i];
		Q[now].push_back(x);dep[now]=i;
	}
}
ll merge(int x,ll t)
{
     
	ll as=0;
	for(int i=dep[x]-1; i>=0; i--)
		{
     
			int flag=(bool)((t>>i)&1);
			if(ch[x][flag]) x=ch[x][flag];
			else as+=pw[i],x=ch[x][!flag];
		}
	return as;
}
ll query(int rt)
{
     
	if(Q[rt].size()==1) return 0;
	if(ch[rt][0]) ans+=query(ch[rt][0]);
	if(ch[rt][1]) ans+=query(ch[rt][1]);
	if(!ch[rt][0]||!ch[rt][1]) return 0;
	int flag=0;
	if(Q[ch[rt][0]].size()>Q[ch[rt][1]].size()) flag=1;
	int sz=Q[ch[rt][flag]].size();
	ll tmp=2e15;
	for(int i=0; i<sz; i++)
		tmp=min(tmp,merge(ch[rt][flag^1],Q[ch[rt][flag]][i]));
	return tmp+pw[dep[rt]-1];
}
int main()
{
     
	cin>>n;
	pw[0]=1;
	for(int i=1; i<=30; i++) pw[i]=pw[i-1]*2;
	for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
	sort(a+1,a+n+1);
	n=unique(a+1,a+n+1)-a-1;
	for(int i=1; i<=n; i++) insert(a[i]);
	ans+=query(0);
	printf("%lld",ans);
}

你可能感兴趣的:(【Borůvka】【CF888G】)