https://ac.nowcoder.com/acm/contest/5670/B
题意
给一棵树,每条边有边权。可以任意加边和删边,但要满足任何时刻图连通,而且任何一个环的边权异或和为0。求操作后最小权值和
题解
任意两点间连边的权值是固定的,可以预处理给每个点赋值点权,两点间的边权就是点权的异或,点权直接dfs一遍取链上的异或和即可。
然后就是异或最小生成树模板
对于异或最小生成树,有Boruvka算法,先对于每个点,选择在所有与之相连的边中,权值最小的边,并将这条边加入到最小生成树中。显然这样连出来的边会形成一个森林,并且连边后连通块个数至少减半。然后我们将每个连通块再看成一个点,重复以上算法即可。时间复杂度O(mlogn)。
对于此题,我们把所有点权扔到Trie里,对于每一层,有两种情况,
-
一种全为0,或者全为1,那么这一位无需考虑,不会对答案产生贡献。
-
一部分为0,一部分为1,那么两组之间要有一条边把这两个组相连,在字典树上找到异或值最小的作为连接两个集合的边,加入答案。对两组分别递归求解。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct READ {
inline char read() {
#ifdef _WIN32
return getchar();
#endif
static const int IN_LEN = 1 << 18 | 1;
static char buf[IN_LEN], *s, *t;
return (s == t) && (t = (s = buf) + fread(buf, 1, IN_LEN, stdin)), s == t ? -1 : *s++;
}
template <typename _Tp> inline READ & operator >> (_Tp&x) {
static char c11, boo;
for(c11 = read(),boo = 0; !isdigit(c11); c11 = read()) {
if(c11 == -1) return *this;
boo |= c11 == '-';
}
for(x = 0; isdigit(c11); c11 = read()) x = x * 10 + (c11 ^ '0');
boo && (x = -x);
return *this;
}
} in;
const int N = 1e5 + 50;
int a[N];
struct node {
int v, w;
node(int v = 0, int w = 0): v(v), w(w) {}
};
vector<node> G[N];
int cnt = 0;
void dfs(int u, int fa) {
for (auto nx : G[u]) {
if (nx.v == fa) continue;
a[nx.v] = a[u] ^ nx.w;
dfs(nx.v, u);
}
}
int trie[N << 5][2];
int tot = 0;
void ins(int x) {
int rt = 0;
for (int i = 30; i >= 0; i--) {
int now = (x >> i) & 1;
if (!trie[rt][now]) trie[rt][now] = ++tot;
rt = trie[rt][now];
}
}
int qry(int x) {
int ans = 0, rt = 0;
for (int i = 30; 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;
}
ll ans = 0;
void calc(int l, int r, int dep) {
if (dep < 0 || l >= r) return;
int mid = l - 1;
while (mid < r && !((a[mid + 1] >> dep) & 1)) mid++;
calc(l, mid, dep - 1);
calc(mid + 1, r, dep - 1);
if (mid == l - 1 || mid == r) return;
tot = 0;
for (int i = l; i <= mid; i++) ins(a[i]);
int res = 0x7fffffff;
for (int i = mid + 1; i <= r; i++) res = min(res, qry(a[i]));
ans += res;
for (int i = 0; i <= tot; i++) trie[i][0] = trie[i][1] = 0;
}
int main() {
int n; in >> n;
for (int i = 1; i < n; i++) {
int u, v; ll w;
in >> u >> v >> w;
u++; v++;
G[u].push_back(node(v, w));
G[v].push_back(node(u, w));
}
dfs(1, 0);
sort(a + 1, a + n + 1);
calc(1, n, 30);
printf("%lld
", ans);
return 0;
}