【模板】异或最小生成树

主要是借用了最小生成树的Boruvka算法的思想:
在两个连通块内找到一条最短的路径,连接两个连通块合并成一个连通块


那些年我用异或最小生成树做过的题:

  • cf888g
  • 2020牛客暑期多校训练营(第五场)B Graph

板子

#include 

using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int n, m, k;

namespace XorMST {
    int a[N]; // 原数组
    int trie[N * 30][2]; // 字典树
    int tot;//编号

    // 将x以二进制的形式插入到01字典树里
    void Insert(int x) {
        int rt = 0;
        for (int i = 29; i >= 0; i--) {
            int now = (x >> i) & 1; // 拆分x的每一位
            if (!trie[rt][now])
                trie[rt][now] = ++tot;
            rt = trie[rt][now];
        }
    }

    // 在字典树上找x能异或出来的最小值
    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;
    }

    // 对a[l...r]进行分治
    // dep 当前二进制搜索到的位置
    ll ans = 0;

    void dfs(int l, int r, int dep) {
        if (dep == -1 || l >= r) return;
        int mid = l - 1;
        // 以二进制第dep位置上的数字划分为两个集合 左边0 右边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;

        //将左边集合里的值 插入到01字典树中
        for (int i = l; i <= mid; i++) {
            Insert(a[i]);
        }

        // 找最小边
        // 根据题意描述 边的权值和边的两个端点有关 E(i,j)=a[i]^a[j]
        // 显然是在右边集合里取一个值 去左边集合里找异或最小值
        int tmp = INT_MAX;
        for (int i = mid + 1; i <= r; i++) {
            tmp = min(tmp, Search(a[i]));
        }

        //统计答案
        ans += tmp;

        //清空字典树
        for (int i = 0; i <= tot; i++) {
            trie[i][0] = trie[i][1] = 0;
        }
        tot = 0;
    }
}
using namespace XorMST;


void solve_cf888g() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    sort(a + 1, a + 1 + n);//必须要排序
    dfs(1, n, 29);

    cout << XorMST::ans << endl;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    solve_cf888g();
    
    return 0;
}

你可能感兴趣的:(板子)