求调
  • 板块CF888G Xor-MST
  • 楼主Mike_666
  • 当前回复0
  • 已保存回复0
  • 发布时间2024/9/10 18:32
  • 上次更新2024/9/10 21:22:06
查看原帖
求调
419327
Mike_666楼主2024/9/10 18:32

Boruvka算法+01trie树 现在在#3上re,可能访问到了负数节点

#include<bits/stdc++.h>
using namespace std;
/*
对全部点建一个trie树 
对于一个联通块,建立一个trie树,只包含当前联通块里的节点 
每次查询一个联通块里的一个点x到其他联通块里的最小异或值,
就是在整个trie树中选择一个节点,使得与a[x]异或值最小(按位计算) 
注:使用联通块trie树当前子树的大小和
大trie树当前子树的大小作比较 去除选择当前联通块里的点 
合并两个联通块时,就要合并两个trie树 
*/
const int N=2e5+5; 
struct node{//trie树节点
	int ch[2],siz,point;//ch:孩子编号,-1为没有孩子 siz:子树大小 point:该权值对应的节点编号 
	node(int si,int pi){
		siz=si,point=pi;
		ch[0]=ch[1]=-1;
	}
}; 
int n,a[N]; 
int f[N],minn[N],other[N];//minn:每个点的最小边长 other:对应的另一个节点 
int root[N];//root:每个联通块trie树的根,0表示全部点的trie树
vector<node>trie;//所有trie树的所有节点 
void insert(int x,int pnt,int w){//x:当前根,pnt:该权值对应节点 
	int t;
	trie[x].siz++;
	for(int i=30;i>=0;i--){
		t=(w>>i)&1;//该位w的子树选取 
		if(trie[x].ch[t]==-1){
			trie.push_back(node(1,-1));
			x=trie[x].ch[t]=trie.size()-1;
		}
		else{ 
			x=trie[x].ch[t];
			trie[x].siz++;
		}
	} 
	trie[x].point=pnt;//存入该权值对应节点 
} 
inline int getsizec(int x,int id){
	if(x<0)return 0;
	if(trie[x].ch[id]<0)return 0;
	return trie[trie[x].ch[id]].siz;
}
inline int getpoint(int x){
	if(x<0)return -1;
	return trie[x].point;
}
pair<int,int> query(int r1,int r2,int w){//r1:大trie树,r2:要去除的trie树,w:和另一个值异或
	int t,ans=0;
	for(int i=30;i>=0;i--){
		t=(w>>i)&1;//该位w 
		if(trie[r1].ch[t]!=-1&&trie[trie[r1].ch[t]].siz-getsizec(r2,t)>0){//若可以与t相同,则异或为0,就选它 
		//  表示有这个子树     表示:在大trie树的当前子树中有除了当前联通块的节点 
			r1=trie[r1].ch[t];//跳到子树 
			r2=trie[r2].ch[t];
//			cout<<i<<'\n';
		}
		else{
//			cout<<i<<'t'<<endl;
			r1=trie[r1].ch[t^1];//跳到另一个子树 
			r2=trie[r2].ch[t^1];
//			if(r1<0)cout<<"x1"<<r1<<endl;
//			if(r2<0)cout<<"x2"<<r2<<endl;
			ans=ans|(1<<i);//加入最终异或值 
		}
	} 
	return make_pair(ans,getpoint(r1));//能达到的最小异或值,即其对应节点 
}
void merge(int &r1,int r2){//合并两棵trie
	if(r1==-1){
		r1=r2;
		return ;
	}
	if(r2==-1){
		r1=r1;
		return ;
	}
	merge(trie[r1].ch[0],trie[r2].ch[0]);
	merge(trie[r1].ch[1],trie[r2].ch[1]);
	trie[r1].siz=getsizec(r1,0)+getsizec(r1,1);
//	trie[r1].point=trie[r2].point;
	return ;
}
int findf(int x){
	if(f[x]==x)return x;
	f[x]=findf(f[x]);
	return f[x];
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	sort(a+1,a+n+1);
	n=unique(a+1,a+n+1)-a-1;
	for(int i=0;i<=n;i++){//建总根
		trie.push_back(node(0,-1));
		root[i]=trie.size()-1; 
	}
	for(int i=1;i<=n;i++){
		insert(root[0],i,a[i]);
		insert(root[i],i,a[i]);
	}
	for(int i=1;i<=n;i++)f[i]=i;
	int fu,fv,ischanged,cnt=0,ans=0;
	while(true){
		memset(minn,0x3f,sizeof(minn));
		ischanged=false;
		for(int i=1;i<=n;i++){
			fu=findf(i);
			pair<int,int> t=query(root[0],root[fu],a[i]);//查找最小边 
			if(t.second==-1)continue;
			fv=findf(t.second);
			if(fu==fv)continue;
			ischanged=true;
			if(minn[fu]>t.first)minn[fu]=t.first,other[fu]=fv;
			if(minn[fv]>t.first)minn[fv]=t.first,other[fv]=fu;
		}
		if(!ischanged)break;
		ischanged=false;
		for(int i=1;i<=n;i++){
			fu=findf(i),fv=findf(other[i]);
			if(minn[i]<0x3f3f3f3f&&fu!=fv){
				ischanged=true;
				cnt++;
				ans+=minn[i];
				merge(root[fu],root[fv]);
				f[fv]=fu;
			} 
		}
		if(!ischanged)break;
		if(cnt==n-1)break; 
	}
	printf("%d",ans);
	return 0;
}
2024/9/10 18:32
加载中...