G. Xor-MST (分治+Trie树)

G. Xor-MST time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output

You are given a complete undirected graph with n vertices. A number ai is assigned to each vertex, and the weight of an edge between vertices i and j is equal to aixor aj.

Calculate the weight of the minimum spanning tree in this graph.

Input

The first line contains n (1 ≤ n ≤ 200000) — the number of vertices in the graph.

The second line contains n integers a1a2, …, an (0 ≤ ai < 230) — the numbers assigned to the vertices.

Output

Print one number — the weight of the minimum spanning tree in the graph.

Examples input Copy

5
1 2 3 4 5

output Copy

8

input Copy

4
1 2 3 4

output Copy

8

思路:问题关键在于如何划分顶点为不同的集合,使得每个集合中的顶点权重异或值是最小的。根据位操作的性质,可以从高位到低位依次取相似度最大的数字,每一位都把相同的数字归为一个集合,递归下去。

如二进制数字{1,10,11,100,101,110},首先划分为:{{1,10,11},{100,101,110}} ,后一个集合再次划分为{{100,101},110}等;合并两个集合时,把一个集合中的数字都添加到一个二叉树中,枚举另一个集合中的所有数字和这个二叉树中所有值的最小异或值。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,n) for(int i=(a);i<(n);++i)
#define per(i,a,n) for(int i=(n-1);i>=(a);--i)
#define pb push_back
#define mp make_pair
const int Inf=1e9;
#define N 200010
#define M 26
int a[N];
ll sum=0;
int rt[N*30][2];
int cnt;
void Insert(int x){
    int k=0;
    for(int i=29;i>=0;--i){
        int id=1&(x>>i);
        if(rt[k][id]==0){
            rt[k][id]=cnt++;
            k=rt[k][id];
            rt[k][0]=rt[k][1]=0;
        }
        else{
            k=rt[k][id];
        }
    }
}

int find_val(int x){   //查找与x异或值最小的元素
    int val=0,k=0;
    for(int i=29;i>=0;--i){
        int id=1&(x>>i);
        if(rt[k][id]==0) id^=1;
        if(id){
            val|=(1<<i);
        }
        k=rt[k][id];
    }
    return val; //当val==x时,x^val=0
}

void dfs(int id,int l,int r){
    if(id<0) return ;   //(二进制)从最高位开始,一直划分到个位
    int mid=l;
    for(int i=l;i<r;++i){
        if((a[i]>>id)&1){
            mid=i;
            break;
        }
    }
    if(mid<r) dfs(id-1,mid,r);   // (1<<id)==1的划分为一个集合
    if(l<mid) dfs(id-1,l,mid);   // (1<<id)==0的划分为一个集合
    if(l<mid&&mid<r){    //id==0时,只有个位不同,异或值为1
        cnt=0;
        rt[cnt][0]=rt[cnt][1]=0; ++cnt;    //同一数据结构重复使用,记得初始化为0
        for(int i=l;i<mid;++i) Insert(a[i]);
        int val=(1<<30);
        for(int i=mid;i<r;++i) val=min(val,a[i]^find_val(a[i]));
        sum+=val;
    }
}
int main(){
    ios::sync_with_stdio(0);
    int n;
    cin>>n;
    for(int i=0;i<n;++i){
        cin>>a[i];
    }
    sort(a,a+n);   //排序为下一步根据大小划分集合
    dfs(29,0,n);
    cout<<sum;
    return 0;
}

    原文作者:Trie树
    原文地址: https://blog.csdn.net/u011721440/article/details/80990405
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞