题目:

个点的无向完全图,每个点有点权,边权是点权的值。问最小生成树边权和。


做法:

最小生成树,经典老题了。做法是字典树上启发式合并。
字典树上,按在左在右顺序,从左到右叶子表示的数的大小是有序的。所以我们先对点权从小到大排序,然后依次插入字典树上。这样字典树的子树下表示的数都来自原数组的一段连续区间,这便于我们处理。我们从高位到低位递归处理,先将子树和子树下的点各自分别联通,然后在这两个联通块上建一条边将其合并。这条边怎么选呢?我们可以枚举一棵子树下的所有数,在另一棵子树里查和其值最小的另一个数,这两个数建边的边权就是这个值。我们选边权最小的建边即可。而这个过程中,我们每次都枚举比较小的子树,则枚举的规模的,不会


代码:

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define debug(a) cout << #a ": " << a << endl
using namespace std;
typedef long long ll;
const int N = 2e5 + 7;
int a[N];
struct Trie{
    static const int N = 5e6 + 7;
    int tot, ch[N][2], val[N], in[N], out[N];
    void insert(int x, int dep, int rt, int id){
        if (dep == -1){
            val[id] = x;
            in[rt] = out[rt] = id; return;
        }
        int b = (x>>dep)&1;
        if (!ch[rt][b]) ch[rt][b] = ++tot;
        insert(x, dep-1, ch[rt][b], id);
        in[rt] = ch[rt][0] ? in[ch[rt][0]] : in[ch[rt][1]];
        out[rt] = ch[rt][1] ? out[ch[rt][1]] : out[ch[rt][0]];
    }
    int ask(int x, int dep, int rt){
        if (dep == -1) return 0;
        int b = (x>>dep)&1;
        if (ch[rt][b]) return ask(x, dep-1, ch[rt][b]);
        else return ask(x, dep-1, ch[rt][b^1])+(1<<dep);
    }
    ll solve(int dep, int rt){
        if (dep == -1) return 0;
        ll ans = 0;
        int ls = ch[rt][0], rs = ch[rt][1];
        if (ls) ans += solve(dep-1, ls);
        if (rs) ans += solve(dep-1, rs);
        if (!ls || !rs) return ans;
        ll tmp = 1e17;
        if (out[ls]-in[ls] <= out[rs]-in[rs]){
            for (int i = in[ls]; i <= out[ls]; ++i){
                tmp = min(tmp, (ll)ask(val[i], dep-1, rs)+(1<<dep));
            }
        }else{
            for (int i = in[rs]; i <= out[rs]; ++i){
                tmp = min(tmp, (ll)ask(val[i], dep-1, ls)+(1<<dep));
            }
        }
        return ans+tmp;
    }
}trie;
int main(void){
    IOS;
    int n; cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    sort(a+1,a+n+1);
    for (int i = 1; i <= n; ++i) trie.insert(a[i], 30, 0, i);
    cout << trie.solve(30, 0) << endl;
    return 0;
}