2018.01.03【CodeForces888G】Xor-MST(最小xor生成树)(01TRIE)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/85676087

传送门


解析:

随手一写就CF速度rk1了啊。。。

思路:

好像是说有一个叫什么Borůvka算法的东西。没学过,不过好像我yy的这种解法就是优化过的Borůvka。。。


朴素的Borůvka简要说明:

1.用数组记录每个子树的最近邻居。(类似Prim)
2.对于每一条边进行处理(类似Kruskal)
如果这条边连成的两个顶点同属于一个集合,则不处理,否则检测这条边连接的两个子树,如果是连接这两个子树的最小边,则更新

由于每次循环迭代时,每棵树都会合并成一棵较大的子树,因此每次循环迭代都会使子树的数量至少减少一半,所以,循环迭代的总次数为 O ( l o g n ) O(logn) 。每次循环迭代所需要的计算时间:第2步,每次检查所有边 O ( m ) O(m) ,去更新每个连通分量的最小弧;第3步,合并个子树。所以总的复杂度为 O ( E l o g V ) O(E*logV)


懂行的都看得出来,这个算法一般情况下只适合用来做并行式计算,然而比赛就不要想着并行式计算了。

而这里显然不需要检查所有子树。复杂度得到一定程度的优化。

子集xor问题考虑线性基,数对xor问题考虑01Trie。

但是这道题01Trie需要处理一下,记录它辖域内有哪些值,不然接下来会很麻烦。
对于这步,我们只需要排一个序,然后插入的时候记录一下路过该节点的最大最小id就行了,因为排序后的值转成二进制后的前缀LCP从某一个位置开始向两个方向都是单调不减的。

然后我们在Trie上进行一次dfs。
对于每个子树,由于我们要求异或和最小,考虑让它们前缀LCP尽量长,显然将其内部的节点与其他子树合并后再合并回来不会更优。

那么直接递归处理子树。
然后如果左右子树都不为空,我们要考虑合并左右子树。
刚才提到的记录辖域就是为了这个。
我们将左子树所有值全部丢到右子树上跑一遍最小xor,取最小中的最小,就是合并左右子树中的点的最小代价。

时间复杂度 O ( n log 2 A ) O(n\log^2|A|)


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc putchar
#define cs const

namespace IO{
	namespace IOONLY{
		cs int Rlen=1<<18|1;
		char buf[Rlen],*p1,*p2;
	}
	inline char get_char(){
		using namespace IOONLY;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:(*p1++);
	}
	
	inline int getint(){
		re int num;
		re char c;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

cs int N=200005,bit=29;
int a[N];ll ans;
struct _01TRIE{
	int son[N*(bit+2)][2],L[N*(bit+2)],R[N*(bit+2)],siz;
	_01TRIE(){}
	
	inline void insert(int val,int id){
		int now=0;
		for(int re i=bit;~i;--i){
			re bool f=(val>>i)&1;
			if(!son[now][f]){
				son[now][f]=++siz;
				L[siz]=R[siz]=id;
			}
			now=son[now][f];
			L[now]=min(L[now],id);
			R[now]=max(R[now],id);
		}
	}
	
	inline int query(int u,int val,int bit){
		for(int re i=bit;~i;--i){
			if(L[u]==R[u])return a[L[u]];
			re bool f=(val>>i)&1;
			if(son[u][f])u=son[u][f];
			else if(son[u][!f])u=son[u][!f];
			else return 0;
		}
		return a[L[u]];
	}
	
	inline void dfs(int now,int bit){
		if(!bit){
			if(son[now][0]&&son[now][1])ans+=a[L[son[now][0]]]^a[L[son[now][1]]];
			return ;
		}
		if(son[now][0])dfs(son[now][0],bit-1);
		if(son[now][1])dfs(son[now][1],bit-1);
		if(son[now][0]&&son[now][1]){
			re int tmp=0x7fffffff;
			for(int re i=L[son[now][0]],r=R[son[now][0]];i<=r;++i)
			tmp=min(tmp,query(son[now][1],a[i],bit-1)^a[i]);
			ans+=tmp;
		}
	}
}Trie;

int n;
signed main(){
	n=getint();
	for(int re i=1;i<=n;++i)a[i]=getint();
	sort(a+1,a+n+1);
	for(int re i=1;i<=n;++i)Trie.insert(a[i],i);
	Trie.dfs(0,bit);
	printf("%lld",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/85676087