G. Xor-MST(01字典树 Boruvka算法)

题目

链接

题意

给n个点的点权,在该完全图上求最小生成树。两点之间的边权为两个点权的异或值。

思路

完全图,n <= 1e5,跑 prim 或者 kruscal 会T。

因为异或,考虑01字典树

树根为高位,叶子为地位。

建一个深度为 30 的01字典树

左右两个子树就是两个连通块,从两个子树里面选点相连,要连边的权值尽可能小就是让公共前缀尽可能的长。

可以看到就是Boruvka算法的思想。

代码

#include 
using namespace std;
typedef long long ll;
const int N = 7e6 + 10;
int n, a[N];
vector <int> v[N];
struct trie{
    int cnt, t[N][2];//30*2e5
    void insert(int z, int x, int y){
        v[z].push_back(y);
        if(x < 0)
            return ;
        bool st = 1 << x & y;
        if(!t[z][st])
            t[z][st] = ++cnt;
        insert(t[z][st], x - 1, y);
    }
    int query(int z, int x, int y){
        if(x < 0)
            return 0;
        bool st = 1 << x & y;
        if(t[z][st])
            return query(t[z][st], x - 1, y);
        else
            return query(t[z][st ^ 1], x - 1, y) + (1 << x);
    }
}g;
ll gao(int o, int q){
    if(v[o].size() <= 1)
        return 0;
    ll ans = 0;
    for(int i = 0; i < 2; i++){
        if(g.t[o][i])
            ans += gao(g.t[o][i], q - 1);
    }
    if(!g.t[o][0] || !g.t[o][1])
        return ans;

    int x = g.t[o][0], y = g.t[o][1];
    if(v[x].size() > v[y].size())
        swap(x, y);
    ll cm = 1e18;
    for(auto k:v[x]){//枚举两个子树中少的,在另一个选择
        cm = min(cm, 1ll * g.query(y, q - 1, k));
    }
    ans += cm + (1 << q);
    return ans;
}
int main() {
    scanf("%d", &n);
    g.cnt = 1;
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for(int i = 1; i <= n; i++)
        g.insert(1, 30, a[i]);
    printf("%lld\n", gao(1, 30));
    return 0;
}

参考来源

链接

你可能感兴趣的:(数据结构,树)