题目大意
给出一颗树,有两种操作,添加一条边,删除一条边。
每个时刻必须满足
如果有环那么环的边权异或和必须是0,
必须是联通的。
解题思路
任意两个点之间连边的权值都是固定的,由于图需要始终联通,所以两点间始终存在至少一条路径,如果存在多条,根据环的异或和为0,两点间的路径的异或和应该相等,且始终是固定的。(摘自官方题解) 这样么,就可以每个点都记录一个权值,而两点间连边的权值,就是两端点权值的异或值。
接下来就是求异或最小生成树的问题了
当求一些数的最大或最小异或值,就可以联想到字典树:在字典树中两个数的最近公共祖先越近,两个数的异或值就越小
对于树中某一节点p,p的左子树内部异或绝对优于p的左子树中的任意一点异或p的右子树中的任意一点, 右子树同理。
在求以p为根节点的最小异或生成树生成树时,明显就是左子树内部求+右子树内部求+左右子树中的某两点互相连接成树。
这里明显用到分治思想, 前两项可以递归算出,最后一项可以暴力枚举求出:
以左子树为标准,枚举右子树的所有数与左子树的数异或,求一个min。右子树与之一样
到这里还有一个问题,就是怎样知道某一个节点下的左右子树中分别有哪些数?
这里的解决方案就是首先对所有的点值排个序,这样在建树的时候它的顺序是从左到右,
用两个数组,l[i]表示数的二进制表示中包含i这个节点在序列中下标的左端点,r[i]就是右端点
代码区
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; i ++ )
#define clr(x, y) memset(x, y, sizeof (x))
#define one first
#define two second
#define LL long long
#define PII pair <int, int>
inline bool read(LL &num) {
char in;
bool IsN=false;
in=getchar();
if(in==EOF) return false;
while(in!='-'&&(in<'0'||in>'9')) in=getchar();
if(in=='-') {
IsN=true;
num=0;
} else num=in-'0';
while(in=getchar(),in>='0'&&in<='9') {
num*=10,num+=in-'0';
}
if(IsN) num=-num;
return true;
}
const int N = 2e5 + 7;
vector<PII> G[N];
int T[N*30][2], idx;
int a[N];
int l[N*30], r[N*30];
void add(int x, int id) {
int p = 0;
for (int i = 29; i >= 0; i -- ) {
int &s = T[p][x >> i & 1];
if (!s) {
s = ++idx;
l[idx] = id;
}
p = s;
r[p] = id;
}
}
LL find(int p, int pos, int x) {
LL res = 0;
for (int i = pos; i >= 0; i --) {
int s = x >> i & 1;
if (T[p][s]) p = T[p][s];
else {
p = T[p][!s];
res += (1 << i);
}
}
return res;
}
LL query(int p, int pos) {
if (T[p][0] && T[p][1]) {
int x = T[p][0], y = T[p][1];
LL minv = 1e17;
rep(i, l[x], r[x])
minv = min(minv, find(y, pos - 1, a[i]) + (1 << pos));
return minv + query(T[p][0], pos - 1) + query(T[p][1], pos - 1);
} else if (T[p][0]) return query(T[p][0], pos - 1);
else if (T[p][1]) return query(T[p][1], pos - 1);
return 0;
}
void dfs(int u, int f) {
// cout << a[u] << " " << u << endl;
rep(i, 0, G[u].size() - 1) {
// cout << "dsadas" << endl;
int v = G[u][i].one;
LL w = G[u][i].two;
if (v == f) continue;
a[v] = a[u] ^ w;
// cout << a[v] << " " << v << endl;
dfs(v, u);
}
}
int main () {
int n;
scanf ("%d", &n);
rep(i, 2, n) {
int u, v, w;
scanf ("%d%d%d", &u, &v, &w);
u ++, v ++;
G[u].push_back({v, w});
G[v].push_back({u, w});
}
dfs(1, -1);
sort(a + 1, a + 1 + n);
rep(i, 1, n) add(a[i], i);
printf ("%lld\n", query(0, 29));
return 0;
}