zoukankan      html  css  js  c++  java
  • [Codeforces 888G]Xor-MST

    Description

    题库链接

    给你 (n) 个节点,每个节点带有点权 (a) 。点之间两两有连边,边 ((u,v)) 的边权为 (a_u oplus a_v) 。求该图的最小生成树。

    (1leq nleq  200000,0leq a_i<2^{30})

    Solution

    (Kruskal) 的思想,我们一定还是每次选最小的边连接会最优。

    考虑边的权值最小,由按位贪心的原则,显然最好让二进制低位的数值不同。

    首先需要让高位相同,我们将所有权值转为二进制,那么就要二进制的前缀相同。

    我们可以将权值插入 (Trie) 中,对于 (Trie) 中的每个节点,如果它有两个儿子,显然就需要在这个节点处合并左右子树(连边)。首先左右子树中的所有点显然是已经在一个连通块内的。我们只需要在左右子树的联通块中各选出一个点,连边即可。

    如何选点?似乎没有更好的方法,那么就启发式合并,取出所有的在个数较小的联通块内的点,每一个都到另外一个联通块的子树中做一次查询,找出最小的边。每个点被选取的期望为 (O(log_2 n))

    时间复杂度: (O(mnlog_2 n))

    空间复杂度: (O(mnlog_2 n))

    值得一提的是其实这种求最小生成树的思想就是 (Boruvka) 算法。

    Code

    #include <bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int N = 200000;
    const LL INF = 1e18;
    
    int n, a[N+5], A[40]; LL bin[40], ans;
    struct Trie {
        int ch[N*35][2], bit[N*35+5], pos;
        vector<int>key[N*35];
        void insert(int t) {
            int u = 0; key[u].push_back(t);
            for (int i = 35; i >= 1; i--) {
                if (ch[u][A[i]] == 0) ch[u][A[i]] = ++pos; u = ch[u][A[i]];
                t -= bin[i-1]*A[i], key[u].push_back(t), bit[u] = i;
            }
        }
        LL qry(int u, int t) {
            LL ans = 0;
            for (int i = 1; i <= bit[u]-1; i++) A[i] = t%2, t /= 2;
            for (int i = bit[u]-1; i >= 1; i--) {
                if (ch[u][A[i]] != 0) u = ch[u][A[i]];
                else u = ch[u][A[i]^1], ans += bin[bit[u]-1];
            }
            return ans;
        }
        LL query(int o) {
            if (key[o].size() == 1) return 0;
            if (ch[o][0]) ans += query(ch[o][0]);
            if (ch[o][1]) ans += query(ch[o][1]);
            if (!ch[o][0] || !ch[o][1]) return 0;
            LL tmp = INF; int flag = 0;
            if (key[ch[o][0]].size() > key[ch[o][1]].size()) flag = 1;
            int sz = key[ch[o][flag]].size();
            for (int i = 0; i < sz; i++) tmp = min(qry(ch[o][flag^1], key[ch[o][flag]][i]), tmp);
            return tmp+bin[bit[o]-2];
        }
    }T;
    
    void work() {
        scanf("%d", &n); bin[0] = 1; for (int i = 1; i <= 35; i++) bin[i] = (bin[i-1]<<1);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        sort(a+1, a+n+1); n = unique(a+1, a+n+1)-a-1;
        for (int i = 1; i <= n; i++) {
            for (int j = 1, t = a[i]; j <= 35; j++) A[j] = t%2, t /= 2; T.insert(a[i]);
        }
        ans += T.query(0); printf("%lld
    ", ans);
    }
    int main() {work(); return 0; } 
  • 相关阅读:
    iOS开发开辟线程总结--NSThread
    iOS开发GCD的简单使用
    iOS开发本地通知
    iOS开发JOSNModel<optional>,<convertondemand>,<index>
    开发iOS百度地图大头针可以重复点击
    iOS开发跳转指定页面
    iOS开发解决 jsonModel 属性跟系统的重复
    检测是否IE浏览器
    String.prototype运用
    C#读写XML
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/9246547.html
Copyright © 2011-2022 走看看