Xor-MST(CodeForces - 888G)(01Tire树+贪心+Boruvka算法)

文章目录

  • 题目
  • 思路
  • 代码
  • 思考

题目

给你 n n n 个点,每个点点权为 a i a_i ai,定义连接两点花费为 a i a_i ai^ a j a_j aj 求最小生成树?
1 ≤ n ≤ 2 ⋅ 1 0 5 1\le n \le 2\cdot10^5 1n2105

思路

首先我们来看看这个算法:
Boruvka算法
然后如果对于这道题我们用这个算法的话我们会发现,对于一个数的最高位是 0 0 0,它一定会优先和最高位是 0 0 0 的连边再和最高位是 1 1 1 的连边,我们就不难和 01 T i r e 01Tire 01Tire 联系起来

我们首先研究如果将数按照最高位01划分为两个集合 S 1 , S 2 S_1,S_2 S1,S2
Xor-MST(CodeForces - 888G)(01Tire树+贪心+Boruvka算法)_第1张图片
对于某个合并过程而言,我们一定是先 S 1 , S 2 S1,S2 S1S2 点集内分别连接完成后再考虑 S 1 , S 2 S1,S2 S1S2 点集合并,其实用 K r u s k a l Kruskal Kruskal 算法也可以证明,优先选择较小边权进行合并
为了方便实现,我们可以将所有数 s o r t sort sort 后,一个点就能对应一段区间数 L u , R u L_u,R_u Lu,Ru 的从根到 u u u 之间的边的那几位都相等
合并是将 S 1 S1 S1 内的数一个一个到 S 2 S2 S2 01 T i r e 01Tire 01Tire 内查询最小异或和(尽量走相同的)即可

代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
using namespace std;
int read(){
	int f=1,x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return f*x;
}
#define MAXN 200000
#define INF 0x3f3f3f3f
const int bit=30;
int a[MAXN+5];
int n,ch[bit*MAXN][2],L[bit*MAXN+5],R[bit*MAXN+5],Root,ncnt;
void Insert(int val,int pos){//插入数和对应位置
	int u=Root;
	for(int i=bit;~i;i--){
		int j=((val>>i)&1);
		if(!ch[u][j]) ch[u][j]=++ncnt,L[ncnt]=pos,R[ncnt]=pos;
		L[u]=min(L[u],pos),R[u]=max(R[u],pos);
		u=ch[u][j];
	}
	return ;
}
LL Query(int u,int nbit,int val){//对于u子树查找对于val的最小异或值
	LL ret=0;
	for(int i=nbit;~i;i--){
		int j=((val>>i)&1);
		if(ch[u][j]) u=ch[u][j];
		else ret|=(1<<i),u=ch[u][j^1];
	}
	return ret;
}
LL DFS(int u,int nbit){
	LL ret=0;
	if(!nbit)
		return (a[L[u]]^a[R[u]]);//(ch[u][0]&&ch[u][1])?(a[L[u]]^a[R[u]]):0
	if(ch[u][0]) ret+=DFS(ch[u][0],nbit-1);
	if(ch[u][1]) ret+=DFS(ch[u][1],nbit-1);
	if(ch[u][0]&&ch[u][1]){
		LL con=INF;//字典树左右连接
		for(int i=L[ch[u][0]];i<=R[ch[u][0]];i++)
			con=min(con,Query(ch[u][1],nbit-1,a[i]));
		ret+=con+(1<<nbit);
	}
	return ret;
}
int main(){
	n=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	sort(a+1,a+n+1);
	L[Root]=1,R[Root]=n;
	for(int i=1;i<=n;i++)
		Insert(a[i],i);
	printf("%lld\n",DFS(Root,bit));
	return 0;
}

思考

3个月前才做过就不会了…要多总结

你可能感兴趣的:(CodeForces,生成树,Tire树)