我们可以这么思考:对这棵树加边,加到完全图,边权为题目里给定的两点间路径的异或和,这样是符合提议中对该图的处理的,之后开始删边,由于要联通,所以就是求该完全图的最小生成树。但是直接加边时间空间都会炸掉,所以这里考虑一类问题:XOR最小生成树,该问题一般给出每个点的点权,然后两点间的边权为两点的异或,然后求该图的最小生成树,解决该问题的办法就是在01Trie上分块块。
于是我们就可以处理这个问题:首先预处理出来结点0到任意一点路径xor异或和,然后就可以作为点权,来处理这个XOR最小生成树问题,在01Trie上分治就行了。
XOR最小生成树问题见CF888G
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=32*(1e5+10);
int trie[maxn][2],tot;
void init(){memset(trie,0,sizeof(trie));tot=1;};
int pos[maxn];
vector<int> oid[maxn];
vector<pair<int,int> > edge[maxn];
int n;
int dis[maxn];
bool vis[maxn];
void insert(int x,int id)
{
int p=1;
for(int i=30;i>=0;i--){
int c=(x>>i)&1;
if(!trie[p][c]) trie[p][c]=++tot;
p=trie[p][c];
}
pos[p]=id;
oid[id].push_back(dis[id]);
}
void pre_dfs(int u)
{
vis[u]=1;
for(int i=0;i<edge[u].size();i++){
int v=edge[u][i].first;
int w=edge[u][i].second;
if(vis[v]) continue;
dis[v]=dis[u]^w;
pre_dfs(v);
}
}
int find(int x,int root,int dep)
{
int p=root;
int ans=0;
for(int i=dep-1;i>=0;i--){
int c=(x>>i)&1;
if(!trie[p][c]){
ans|=(1<<i);
p=trie[p][!c];
continue;
}
p=trie[p][c];
}
return ans|(1<<dep);
}
const int inf=0x7fffffff;
long long ans=0;
void dfs(int root,int dep)
{
if(!root||!dep) return;
int p=root;
int lson=trie[p][0],rson=trie[p][1];
dfs(lson,dep-1);
dfs(rson,dep-1);
if(!lson||!rson){
pos[p]=pos[lson+rson];
return;
}
int lid=pos[lson],rid=pos[rson];
int mn=inf;
if(oid[lid].size()<oid[rid].size()){
for(int i=0;i<oid[lid].size();i++){
mn=min(mn,find(oid[lid][i],rson,dep-1));
oid[rid].push_back(oid[lid][i]);
}
ans+=(long long)mn;
pos[p]=rid;
oid[lid].clear();
}
else{
for(int i=0;i<oid[rid].size();i++){
mn=min(mn,find(oid[rid][i],lson,dep-1));
oid[lid].push_back(oid[rid][i]);
}
ans+=(long long)mn;
pos[p]=lid;
oid[rid].clear();
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n-1;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
u++;v++;
edge[u].push_back({v,w});
edge[v].push_back({u,w});
}
dis[1]=0;
pre_dfs(1);
init();
for(int i=1;i<=n;i++) insert(dis[i],i);
dfs(1,31);
printf("%lld\n",ans);
}