G. Xor-MST 异或边的最小生成树 分治

题目链接:http://codeforces.com/problemset/problem/888/G

 

题意:

        给你1e5个点,每个点有一个权值,两个点之间如果连边,那么边权就是这两个点权异或起来的值,要你求出所有点组成的最小生成树。

做法:

       说实话我真的不可能想的到,哪怕知道了分治的做法,也是花了一段时间去理解的。

       总的来说就是把所有的数字先进行排序,把所有的数字用01字典树进行维护。假设数字 1 2 3 4 -> 01 10 11 100

      用最高不同位将数字进行分开,之后内部的数字再用同样的方法进行异或连边,比如100和01 10 11 从第3位就不同,那么100就要在1 10 11 中找一个进行异或,1和10 11 在第二位不同,之后 10和11先做,后和1做,最后和100做,复杂度是logn的,每次进行查找还需要30,理论上也是不会炸的。

 


#include 
using namespace std;
typedef long long ll;
const ll mod=(ll)1e9+7;
const int maxn=200005;
ll ans=0;
int trie[maxn*30][2],tot,n,a[maxn];
void Insert(int x){
    int rt=0;
    for(int i=29;i>=0;i--){
        int now=(x>>i)&1;
        if(!trie[rt][now]) trie[rt][now]=++tot;
        rt=trie[rt][now];
    }
}
int Search(int x){
    int ans=0,rt=0;
    for(int i=29;i>=0;i--){
        int now=(x>>i)&1;
        if(trie[rt][now]){
            rt=trie[rt][now];
        }
        else{
            rt=trie[rt][now^1];
            ans|=(1<=r) return ;
    int mid=l-1;
    while(mid>dep)&1)==0) mid++;

    dfs(l,mid,dep-1);
    dfs(mid+1,r,dep-1);
    if(mid==l-1||mid==r) return ;
    for(int i=l;i<=mid;i++) {
        Insert(a[i]);
    }
    int tmp=INT_MAX;
    for(int i=mid+1;i<=r;i++) {
        tmp=min(tmp,Search(a[i]));
    }
    ans+=(ll)tmp;
    for(int i=0;i<=tot;i++) {
        trie[i][0]=trie[i][1]=0;
    }
    tot=0;
}
int main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    sort(a+1,a+1+n);

    dfs(1,n,29);
    printf("%lld\n",ans);
}

 

你可能感兴趣的:(思维)