题意:
给定一棵n的节点的树,树边有边权。
一次操作你可以添加或删除一条边,但是操作需要满足以下条件:
1.删边之后图必须连通
2.加边之后如果形成环,那么环的异或和必须为0
现在你可以进行无限次操作,问
数据范围:n<=1e5
解法:
题解:
任意两个点之间连边的权值是固定的,通过操作在满足这个条件的情况下,任意修改图的连边方式。
可以将边权转化为点权,边权等于点权异或和。
由于可以任意修改图的连边方式,那么相当于在n个点带权点的完全图中求最小生成树,
其中每条边的边权为连接的两点的点权异或和。
这样转化之后,题目和"Codeforces888 G. Xor-MST"基本一致:Codeforces888 G. Xor-MST题解
(做法就不重复写一遍了)
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
struct Trie{
int a[maxm*30][2];
int tot=0;
void init(){
for(int i=0;i<=tot;i++){
a[i][0]=a[i][1]=0;
}
tot=0;
}
void add(int x){
int node=0;
for(int i=30;i>=0;i--){
int v=(x>>i&1);
if(!a[node][v])a[node][v]=++tot;
node=a[node][v];
}
}
int ask(int x){
int ans=0;
int node=0;
for(int i=30;i>=0;i--){
int v=(x>>i&1);
if(a[node][v]){
node=a[node][v];
}else{
node=a[node][v^1];
ans+=(1<<i);
}
}
return ans;
}
}T;
vector<pair<int,int> >g[maxm];
int a[maxm];
int n;
int solve(int l,int r,int k){
if(k<0||l>=r)return 0;
int mid=l-1;
while(mid+1<=r&&(a[mid+1]>>k&1)==0)mid++;
if(mid==l-1||mid==r)return solve(l,r,k-1);//全0或者全1
int ans=1e18;
T.init();
for(int i=l;i<=mid;i++)T.add(a[i]);
for(int i=mid+1;i<=r;i++)ans=min(ans,T.ask(a[i]));
return ans+solve(l,mid,k-1)+solve(mid+1,r,k-1);
}
void dfs(int x,int fa,int val){
a[x]=val;
for(auto i:g[x]){
if(i.first==fa)continue;
dfs(i.first,x,a[x]^i.second);
}
}
signed main(){
cin>>n;
for(int i=1;i<n;i++){
int a,b,c;cin>>a>>b>>c;
a++,b++;//把点的编号改成从1开始
g[a].push_back({b,c});
g[b].push_back({a,c});
}
dfs(1,1,0);
sort(a+1,a+1+n);
int ans=solve(1,n,30);
cout<<ans<<endl;
return 0;
}