传送门
解析:
随手一写就CF速度rk1了啊。。。
思路:
好像是说有一个叫什么Borůvka算法的东西。没学过,不过好像我yy的这种解法就是优化过的Borůvka。。。
朴素的Borůvka简要说明:
1.用数组记录每个子树的最近邻居。(类似Prim)
2.对于每一条边进行处理(类似Kruskal)
如果这条边连成的两个顶点同属于一个集合,则不处理,否则检测这条边连接的两个子树,如果是连接这两个子树的最小边,则更新
由于每次循环迭代时,每棵树都会合并成一棵较大的子树,因此每次循环迭代都会使子树的数量至少减少一半,所以,循环迭代的总次数为 。每次循环迭代所需要的计算时间:第2步,每次检查所有边 ,去更新每个连通分量的最小弧;第3步,合并个子树。所以总的复杂度为 。
懂行的都看得出来,这个算法一般情况下只适合用来做并行式计算,然而比赛就不要想着并行式计算了。
而这里显然不需要检查所有子树。复杂度得到一定程度的优化。
子集xor问题考虑线性基,数对xor问题考虑01Trie。
但是这道题01Trie需要处理一下,记录它辖域内有哪些值,不然接下来会很麻烦。
对于这步,我们只需要排一个序,然后插入的时候记录一下路过该节点的最大最小id就行了,因为排序后的值转成二进制后的前缀LCP从某一个位置开始向两个方向都是单调不减的。
然后我们在Trie上进行一次dfs。
对于每个子树,由于我们要求异或和最小,考虑让它们前缀LCP尽量长,显然将其内部的节点与其他子树合并后再合并回来不会更优。
那么直接递归处理子树。
然后如果左右子树都不为空,我们要考虑合并左右子树。
刚才提到的记录辖域就是为了这个。
我们将左子树所有值全部丢到右子树上跑一遍最小xor,取最小中的最小,就是合并左右子树中的点的最小代价。
时间复杂度 。
代码:
#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;
}