cf888 G. Xor-MST Boruvka算法 + tire 树+贪心

很明显的Boruvka题。

现在目的就是快速找到某个联通块连出的权值最小的边。。显然 用字典树不可做。。

我们仔细观察条件,按位异或,自然的想到从高位开始。

把最高位为0的和为1的分成2组,这两组之间连一条权值最小的边,其他内部连,一定是最优解。。(根据Boruvka的思想)

其实不需要知道Boruvka算法,只需要知道这样是对的就行。。证明显然。。

然后分成两个联通块,分治下去搞搞就行。。

最多29层,每层On  复杂度合理

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 2e5+7;
int a[M];
int ti[M*32][2],cnt;//字典树
ll ans;
void in(int x)
{
	int k=0;
	for(int i=29;i>=0;i--)
	{
		int id=(x>>i)&1;
		if(ti[k][id]==0)
		{
			ti[k][id]=++cnt;
			k=cnt;ti[k][0]=ti[k][1]=0;
		}
		else k=ti[k][id];
	}
}
int fd(int x)//找到字典树中存的树与x异或最小 
{
	int k=0,ans=0;
	for(int i=29;i>=0;i--)
	{
		int id=(x>>i)&1;
		if(ti[k][id]==0)id^=1;
		k=ti[k][id];
		if(id)ans|=(1<<i);
	}
	return ans;
}
void dfs(int id,int l,int r)
{
	
	if(id<0)return ;
	int mid=l-1;
	for(int i=l;i<=r;i++)
		if(((a[i]>>id)&1)==0)
			mid=i;
	if(l<=mid)dfs(id-1,l,mid);
	if(mid+1<=r)dfs(id-1,mid+1,r);
	if(l<=mid&&mid+1<=r)//这一步放在dfs前和dfs后都行 
	{
		cnt=0;
		ti[0][0]=ti[0][1]=0;
		for(int i=l;i<=mid;i++)in(a[i]);
		int mi=2e9;
		for(int i=mid+1;i<=r;i++)
			mi=min(mi,a[i]^fd(a[i]));
		ans+=mi;
	}
//	cout<<id<<"  "<<l<<" "<<r<<" "<<ans<<endl; 
}
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int n;
  	cin>>n;
  	for(int i=1;i<=n;i++)cin>>a[i];
  	sort(a+1,a+1+n);
  	dfs(29,1,n);
  	cout<<ans<<endl;
	return 0;
}
发布了284 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/bjfu170203101/article/details/104321068