最小异或生成树学习笔记

最近打的区域赛模拟里用到了最小异或生成树

于是进行了一手学习(

问题背景:

有一个序列,每个点有一个权值ai

取一条边i -> j的代价是aixoraj

问这个图的最小生成树的边权和

前置知识

trie树

算法原理

1、Borůvka算法(用于求解最小生成树)

做法:

开始时,每个点都是一个独立的连通块。每次对于一个连通块,去找最短边,其中边的一个端点在连通块内,而另一个不在,将这些边加入答案的最小生成树中,并且用并查集更新联通状态,直到没有边可以添加。

效率分析:

每次联通块的数量至少会减半,而每次询问要访问整个边集

所以总复杂度是O(|E|logN)

2、最小异或生成树

题目中求解最小生成树,如果按照kruskal来的话需要把所有边都列出来,是n2的效率,显然会T飞

考虑把序列中的数字按照从高位到低位分组,按照该位置是0还是1来分

这个和字典树的操作正好对应

于是我们就发现,如果我们每次要把两个点接起来的话,显然在同一个lca的子树内是最优的,因为它高位的异或全部是0,如果离开这个子树而去取其他子树的点的话,则一定会在更高位置产生贡献,答案不会更优

发现现在问题就变成了 让左子树联通,右子树联通 然后让左右子树联通

左子树和右子树显然是原问题的子问题,现在考虑第三个问题要怎么解决

类似于dfs序的考虑,我们在对这个字典树进行先根遍历的时候,得到的序列正好是从小到大的

于是我们可以先对序列进行排序,此时每个点的子树里所包含的数字反映在原序列上就是一段连续的区间

每次合并左右子树的时候,暴力的把比较小的子树中的每一个权值在另一个trie树上跑即可(求最大/最小异或,trie的经典操作)

 

3、题目

Problem - 888G - Codeforces

模板,就是问题背景描述的问题

复制代码
#include<bits/stdc++.h>
using namespace std;
int N,cnt;
int a[200005],L[31*200005],R[31*200005],Tree[31*200005][3];
void Insert(long long x,int pos){
    int Now=0;
    for (int i=30;i>=0;i--){
        if ((x>>i)&1ll){
            int &nw=Tree[Now][1];
            if (!nw) nw=++cnt;
            if (!L[nw]) L[nw]=pos;
            R[nw]=pos;
            Now=nw;
        }
        else{
            int &nw=Tree[Now][0];
            if (!nw) nw=++cnt;
            if (!L[nw]) L[nw]=pos;
            R[nw]=pos;
            Now=nw;
        }
    }
}
long long query(int Now,int pos,long long x){
    long long ans=0;
    for (int i=pos;i>=0;i--){
        int nw=((x>>i)&1);
        if (Tree[Now][nw]) Now=Tree[Now][nw];
        else Now=Tree[Now][nw^1],ans+=(1ll<<i);
    }
    return ans;
}
long long Getans(int rt,int pos){
    int x=Tree[rt][0],y=Tree[rt][1];
    if (!x&&y) return Getans(y,pos-1);
    else if (!y&&x) return Getans(x,pos-1);
    else if (x&&y) {
        int xx=R[x]-L[x]+1,yy=R[y]-L[y]+1;
        long long ans=1e9+7;
        if (xx<=yy){
            for (int i=L[x];i<=R[x];i++){
                ans=min(ans,query(y,pos-1,a[i])+(1ll<<pos));
            }
        }
        else {
            for (int i=L[y];i<=R[y];i++)
                ans=min(ans,query(x,pos-1,a[i])+(1ll<<pos));
        }
        return Getans(x,pos-1)+Getans(y,pos-1)+ans;
    }
    if (pos==0) return 0;
}
int main(){
    scanf("%d",&N); 
    for (int i=1;i<=N;i++)
        scanf("%d",&a[i]);
    sort(a+1,a+N+1);
    for (int i=1;i<=N;i++) Insert(a[i],i);
    printf("%lld\n",Getans(0,30));
    return 0;
} 
复制代码
posted @   si_nian  阅读(269)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
欢迎阅读『最小异或生成树学习笔记』
点击右上角即可分享
微信分享提示