CF888G Xor-MST

cf

luogu

这题\(prim\)\(kruskal\)似乎都不可做,考虑\(Boruvka\)算法,维护一堆连通块,对于每个连通块每次找出其他连通块和它的最小权值的边,然后只用这些边合并连通块,首先这样子做是对的,因为参考\(prim\),连通块应该用最小权的边和其他连通块合并,并且每次合并连通块数量至少少一半,所以只要做\(logn\)次.至于找其他连通块到它的最小权值的边,可以考虑\(trie\),每次把自己联连通块内的点在\(trie\)上的点删掉,然后枚举连通块内每个点查询与其他点的异或最小值

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=2e5+10;
int rd()
{
	int x=0,w=1;char ch=0;
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
int n,a[N],ff[N],stk[N][3],tp;
int findf(int x){return ff[x]==x?x:ff[x]=findf(ff[x]);}
int q[N],hd,tl,op[N*30],tot;
int s[N*30],ch[N*30][2],id[N*30],rt,tt;
vector<int> e[N];
vector<int>::iterator it;
LL ans;

int main()
{
	n=rd();
	rt=++tt;
	int rs=n-1;
	for(int i=1;i<=n;++i) a[i]=rd();
	sort(a+1,a+n+1);
	for(int i=1;i<=n;++i)
	{
		ff[i]=i;
		int o=rt;
		++s[o];
		for(int j=29;~j;--j)
		{
			int xx=a[i]>>j&1;
			if(!ch[o][xx]) ch[o][xx]=++tt;
			o=ch[o][xx];
			++s[o];
		}
		if(id[o]) ff[id[o]]=i,e[i].push_back(id[o]),--rs; 
		id[o]=i;
	}
	while(rs)
	{
		for(int i=1;i<=n;++i)
			if(findf(i)==i)
			{
				hd=1,q[tl=1]=i;
				while(hd<=tl)
				{
					int x=q[hd++],o=1;
					op[++tot]=o;
					--s[o];
					for(int j=29;~j;--j)
					{
						int xx=a[x]>>j&1;
						o=ch[o][xx];
						--s[o];
						op[++tot]=o;
					}
					for(it=e[x].begin();it!=e[x].end();++it)
						q[++tl]=*it;
				}
				int zz=0,mi=1<<30;
				hd=1,q[tl=1]=i;
				while(hd<=tl)
				{
					int x=q[hd++],o=1;
					for(int j=29;~j;--j)
					{
						int xx=a[x]>>j&1;
						if(s[ch[o][xx]]) o=ch[o][xx];
						else o=ch[o][xx^1];
					}
					if(mi>(a[x]^a[id[o]])) mi=a[x]^a[id[o]],zz=id[o];
					for(it=e[x].begin();it!=e[x].end();++it)
						q[++tl]=*it;
				}
				stk[++tp][0]=i,stk[tp][1]=findf(zz),stk[tp][2]=mi;
				while(tot) ++s[op[tot]],--tot;
			}
		while(tp)
		{
			int x=findf(stk[tp][0]),y=findf(stk[tp][1]),z=stk[tp][2];
			if(x!=y)
			{
				ans+=z;
				ff[y]=x;
				e[x].push_back(y);
				--rs;
			}
			--tp;
		}
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2019-10-04 22:39  ✡smy✡  阅读(130)  评论(0编辑  收藏  举报