[Codeforces] 888G. Xor-MST Boruvka算法/分治+01trie

Solution

经典的异或最小生成树,我所知道的有两个做法,分别介绍一下。
1、最小生成树的Boruvka算法。
这个最小生成树算法大概流程是把开始的 n n n个点视为 n n n个连通块,然后每次每个连通块找到一条连向其他连通块的权值最小的边并连起来,这样每次至少减少一半的联通块数,复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)。那么在这题套用这个算法的话,可以建一个所有数的 01 01 01trie,每次求一个连通块连出去的最小边时在trie上删掉这个连通块中的数,然后枚举连通块中的数,在 01 01 01trie上找异或最小值即可。复杂度是 O ( 30 n log ⁡ n ) O(30n\log n ) O(30nlogn)
2、分治做法。
还是挺巧妙的,按位考虑,按最高位为 0 / 1 0/1 0/1分成两个集合,一定是 0 0 0的集合互相连, 1 1 1的集合互相连,然后两边连一条最小边,那么只要用上面找两个集合中的数异或起来最小值的方法,就能递归下去变成子问题了。复杂度是一样的。

Code(做法一)

#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=200010;
const int inf=2147483647;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
int n,a[Maxn],fa[Maxn];LL ans=0;
int findfa(int x){return ((fa[x]==x)?x:fa[x]=findfa(fa[x]));}
int cnt[Maxn*32],tot=0,son[Maxn*32][2],id[Maxn*32];
void ins(int y)
{
	int x=a[y];
	int cur=0;
	for(int i=29;i>=0;i--)
	{
		int t=(x>>i)&1;
		if(!son[cur][t])son[cur][t]=++tot;
		cnt[cur=son[cur][t]]++;
	}
	id[cur]=y;
}
void del(int y)
{
	int x=a[y];
	int cur=0;
	for(int i=29;i>=0;i--)
	{
		int t=(x>>i)&1;
		cnt[cur=son[cur][t]]--;
	}
}
int v,p;
void query(int x)
{
	int cur=0,y=x;v=0;
	for(int i=29;i>=0;i--)
	{
		int t=(x>>i)&1;
		if(cnt[son[cur][t]])cur=son[cur][t];
		else v|=(1<<i),cur=son[cur][t^1];
	}
	p=id[cur];
}
vector<int>h[2],g[Maxn];
int main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	sort(a+1,a+1+n);n=unique(a+1,a+1+n)-(a+1);
	for(int i=1;i<=n;i++)ins(i);
	for(int i=1;i<=n;i++)fa[i]=i,g[i].push_back(i);
	int o=0;
	for(int i=1;i<=n;i++)h[o].push_back(i);
	while(h[o].size()!=1)
	{
		o^=1;
		for(int i=0;i<h[o^1].size();i++)
		{
			int x=h[o^1][i];
			for(int j=0;j<g[x].size();j++)del(g[x][j]);
			int mn=inf,w;
			for(int j=0;j<g[x].size();j++)
			{
				query(a[g[x][j]]);
				if(v<mn)mn=v,w=p;
			}
			int fx=findfa(x),fy=findfa(w);
			if(fx!=fy)
			{
				fa[fx]=fy;
				ans+=(LL)mn;
			}
			for(int j=0;j<g[x].size();j++)ins(g[x][j]);
		}
		h[o].clear();
		for(int i=1;i<=n;i++)g[i].clear();
		for(int i=1;i<=n;i++)
		{
			int x=findfa(i);
			g[x].push_back(i);
			if(x==i)h[o].push_back(x);
		}
	}
	printf("%lld",ans);
}

你可能感兴趣的:(分治,Trie,最小生成树)