异或边的最小生成树

文章介绍了一种处理权值为异或的边的最小生成树问题的方法。首先对点的权值进行排序,然后利用01字典树进行分治,通过最高位不同进行分割,并进行DFS搜索,以logn的时间复杂度求解,最终得出最小生成树的权值总和。
摘要由CSDN通过智能技术生成

题意:
给你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 <bits/stdc++.h>
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<<i);
        }
    }
    return ans;
}
void dfs(int l,int r,int dep){
    if(dep==-1||l>=r) return ;
    int mid=l-1;
    while(mid<r&&((a[mid+1]>>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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值