题目链接:https://ac.nowcoder.com/acm/contest/5670/B
题目大意:
给你一棵树,你可以删除一些边或者增加一些边,但是在过程中必须保证图联通并且出现的任何一个环的异或和为0
题目思路:
考虑加边成为完全图
那么 如何做到加边过程中 做到出现的环为0呢?
考虑从任意一个根出发,到达u的异或和为x,到达v的异或和为y
那么 u 与 v之间的异或和即为 x^y
所以说只需要在u与v之间增加权值为x^y的边
这样就可以保证上述两个条件并且成为完全图
之后就可以求最小异或生成树了
至于求最小异或生成树,详见:https://blog.csdn.net/qq_43857314/article/details/107645865
Code:
ll n,m,p;
ll a[Maxn];
struct XorTrie{
int dfn = 0;
int t[maxn][2];
int L[maxn],R[maxn];
void _init(){dfn = 0;}
void Insert(ll x,int id){
int rt = 0;
for(int k=32;k>=0;k--){
int op = (x>>k&1ll)?1:0;
if(!t[rt][op]) t[rt][op] = ++dfn;
rt = t[rt][op];
if(!L[rt]) L[rt] = id;
R[rt] = id;
}
}
ll AnswerPos(int rt,int pos,ll x){
ll ans = 0;
for(int i=pos;i>=0;i--){
int op = (x>>i&1ll)?1:0;
if(t[rt][op]) rt = t[rt][op];
else{
rt = t[rt][!op];
ans += (1ll<<i);
}
}return ans;
}
void Traceback(int rt){
printf("%d %d\n",L[rt],R[rt]);
if(t[rt][0]) Traceback(t[rt][0]);
if(t[rt][1]) Traceback(t[rt][1]);
}
ll Divide(int rt,int pos){
if(t[rt][0]&&t[rt][1]){
int x = t[rt][0],y = t[rt][1];
ll minl = INF;
for(int k=L[x];k<=R[x];k++) minl = min(minl,AnswerPos(y,pos-1,a[k])+(1ll<<pos));
return minl+Divide(t[rt][0],pos-1)+Divide(t[rt][1],pos-1);
}
else if(t[rt][0]) return Divide(t[rt][0],pos-1);
else if(t[rt][1]) return Divide(t[rt][1],pos-1);
return 0;
}
}tire;
struct node{
int e,next;
ll w;
}edge[Maxn];
int head[Maxn];
ll cnt = 2;
void addedge(int u,int v,ll w){
edge[cnt] = node{v,head[u],w};
head[u] = cnt++;
}
void dfs(int u,int fa,ll w){
a[u] = w;
for(int i=head[u];i;i=edge[i].next){
int e = edge[i].e;
if(e == fa) continue;
dfs(e,u,w^edge[i].w);
}
}
int main()
{
tire._init();
read(n);
for(int i=1;i<=n-1;i++){
ll x,y,w;read(x);read(y);read(w);
x++;y++;
addedge(x,y,w);
addedge(y,x,w);
}
dfs(1,1,0);
sort(a+1,a+n+1);
for(int i=1;i<=n;i++) tire.Insert(a[i],i);
printf("%lld\n",tire.Divide(0,32));
return 0;
}