题意: 给定个点,每个点的权值为,点和点之间的边的边权为。求由这个点构成的完全图的最小生成树的权值。
数据范围:
题解: 考虑分治法求解最小生成树的思想。那么可以将最小生成树的权值按照每一个二进制位进行分组,然后组内求解,再枚举比较两个组之间的数可以构成的最小权值即可,这里可以用树进行操作降低复杂度至。
具体做法:从最高位开始分组,先处理组与组之间的合并大小,然后再处理各自组内的生成树权值即可。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5 + 10; int son[N * 32][2], idx = 1; int n; vector<int> tmp[N * 32]; ll ans = 0; void insert(int x) { int p = 1; for(int i = 30; i >= 0; --i) { int u = x >> i & 1; if(!son[p][u]) son[p][u] = ++idx; p = son[p][u]; tmp[p].push_back(x); } } ll get(int p, int x, int dep) { if(dep < 0) return 0ll; int u = x >> dep & 1; if(son[p][u]) return get(son[p][u], x, dep - 1); return get(son[p][!u], x, dep - 1) + (1 << dep); } void solve(int p, int dep) { int a = son[p][0], b = son[p][1]; if(a && b) { ll mn = 1e18; if(tmp[a].size() > tmp[b].size()) swap(a, b); for(int i = 0; i < tmp[a].size(); ++i) mn = min(mn, get(b, tmp[a][i], dep - 1)); ans += mn + (1 << dep); } if(a) solve(a, dep - 1); if(b) solve(b, dep - 1); } int main() { scanf("%d", &n); for(int i = 1; i <= n; ++i) { int x; scanf("%d", &x); insert(x); } solve(1, 30); printf("%lld\n", ans); return 0; }