【解题报告】2020牛客暑期多校集训营第五场B题

我们可以这么思考:对这棵树加边,加到完全图,边权为题目里给定的两点间路径的异或和,这样是符合提议中对该图的处理的,之后开始删边,由于要联通,所以就是求该完全图的最小生成树。但是直接加边时间空间都会炸掉,所以这里考虑一类问题: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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值