题:https://ac.nowcoder.com/acm/contest/5670/B
题意:给定初始树[u,v,w],可以对树进行增加任意权重边,和删除任意边,前提是这个过程中图要保证连通且若有环则要保证环异或和为0
分析:可以发现任意两个点之间连边的权值都是固定的。由于图始终联通,所以两点间始终存在至少一条 路径,如果存在多条,根据环的异或和为0,两点间的路径的异或和应该相等,且始终是固定的。
将点权转化为边权,具体就是dfs从根到节点i的路径异或和。因为这样在套入trie树时,若选择连接节点u,v,那么权重就是根节点到u的异或和 异或上 根节点到v的异或和,这样就可表示要连接u,v的权重要是u到v的路径异或和(为了达到”保证环异或和为0“这个条件)

#include<bits/stdc++.h> using namespace std; #define pb push_back #define MP make_pair typedef long long ll; const int mod=1e9+7; const int M=1e5+5; const int inf=0x3f3f3f3f; const ll INF=1e18; int a[M],trie[M*32][2]; int pos[M*32]; vector<int>v[M]; int tot=1; ll ans=0; struct node{ int v,w; }; vector<node>g[M]; void pn(){ cout<<"NO"<<' '; } void py(){ cout<<"YES"<<' '; } void dfs1(int u,int fa){ for(auto it:g[u]){ if(it.v==fa) continue; a[it.v]=a[u]^it.w; dfs1(it.v,u); } } void add(int x,int id){ int now=1; for(int i=30;i>=0;i--){ if(!trie[now][(1&(x>>i))]) trie[now][(1&(x>>i))]=++tot; now=trie[now][(1&(x>>i))]; } pos[now]=id; v[id].pb(x); } ll query(int x,int rt,int dep){ ll res=1<<dep; for(int i=dep-1;i>=0;i--){ int nex=trie[rt][(1&(x>>i))]; if(!nex){ nex=trie[rt][1^(1&(x>>i))]; res|=(1<<i); } rt=nex; } return res; } void dfs(int rt,int dep){ if(!rt||!dep) return ; int lson=trie[rt][0]; int rson=trie[rt][1]; dfs(lson,dep-1); dfs(rson,dep-1); if(!lson||!rson){///没有分支了,说明只剩一个集合了 pos[rt]=pos[lson+rson]; return ; } int rid=pos[rson],lid=pos[lson]; if(v[rid].size()>v[lid].size()){ swap(rid,lid); swap(rson,lson); } ll tmp=1<<30; int m=v[rid].size(); for(int i=0;i<m;i++){ tmp=min(tmp,query(v[rid][i],lson,dep-1)); v[lid].pb(v[rid][i]); } v[rid].clear(); ans+=tmp; pos[rt]=lid; } int main(){ int n; scanf("%d",&n); for(int u,v,w,i=1;i<n;i++){ scanf("%d%d%d",&u,&v,&w); u++,v++; g[u].pb(node{v,w}); g[v].pb(node{u,w}); } dfs1(1,0); sort(a+1,a+1+n); add(a[1],1); for(int i=2;i<=n;i++){ if(a[i-1]!=a[i]) add(a[i],i); } dfs(1,31); printf("%lld ",ans); return 0; }