DTOJ#5019. 一棵树

2 篇文章 0 订阅
2 篇文章 0 订阅

题目

宫水三叶有一棵树,这是一棵大小为 n n n 的树,点从 0 0 0 开始编号。

树有 n − 1 n-1 n1 条边,每条边可以用三个数 ( x i , y i , w i ) (x_i,y_i,w_i) (xi,yi,wi) 来描述,表示连接了 x i , y i x_i,y_i xi,yi 的权值为 w i w_i wi 的边。

宫水三叶想要改造这棵树。她可以做以下两种操作:

  • 加入一条边 ( x , y , w ) (x,y,w) (x,y,w) 。图中可以已经出现连接 ( x , y ) (x,y) (x,y) 的边,即允许有重边。但是三叶需要保证加完边后的图任意一个环的边的异或和都为 0 0 0
  • 删除图中的一条边。但是三叶需要保证删除完的图联通。

特别注意的是,对于一个 n n n 个点 n − 1 n-1 n1 条边的连通图是不存在环的,这样一定满足所有环的异或值都为 0 0 0 的限制。

三叶可以重复进行操作,她想知道她最后改造出来的图的边权和最小是多少。

数据范围

本题采用捆绑测试。

对于所有数据,满足 2 ≤ n ≤ 1 0 5 , 0 ≤ x i , y i < n , x i ≠ y i , 0 ≤ w i < 2 30 2\le n \le 10^5,0\le x_i,y_i < n,x_i\neq y_i,0\le w_i <2^{30} 2n105,0xi,yi<n,xi=yi,0wi<230

子任务编号 n n n w i w_i wi分值
1 1 1 ≤ 500 \le 500 500 < 2 30 <2^{30} <230 20 20 20
2 2 2 ≤ 5000 \le 5000 5000 < 2 30 <2^{30} <230 25 25 25
3 3 3 ≤ 1 0 5 \le 10^5 105 < 2 12 <2^{12} <212 25 25 25
4 4 4 ≤ 1 0 5 \le 10^5 105 < 2 30 <2^{30} <230 30 30 30

题解

考虑把 ( i , j ) (i,j) (i,j)连起来,其代价为 ( i , j ) (i,j) (i,j)在树上之间路径的权值和,所以我们可以在把他建成完成图(不需要真的建),然后在上面跑最小生成树即可。
至于跑最小生成树这一块,我们将其放进01trie树里贪心即可,深度越深的异或和一定越小。

代码

#include<iostream>
#include<vector>
using namespace std;
vector<int> X;
int n,tot,cnt,root,head[100010],to[200010],w[200010],nxt[200010],ch[2000010][2];
void adde(int u,int v,int W)
{
	nxt[++tot]=head[u],to[tot]=v,w[tot]=W,head[u]=tot;
}
void dfs(int x,int fa)
{
	for(int i=head[x];i;i=nxt[i]) if(to[i]!=fa) X[to[i]-1]=X[x-1]^w[i],dfs(to[i],x);
}
void addt(int &p,int x,int w)
{
	if(!p) p=++cnt,ch[p][0]=ch[p][1]=0;
	if(w<0) return;
	addt(ch[p][(x>>w)&1],x,w-1);
}
long long ask(int p,int x,int w)
{
	if(w<0) return 0;
	if(ch[p][(x>>w)&1]) return ask(ch[p][(x>>w)&1],x,w-1);
	return ask(ch[p][!((x>>w)&1)],x,w-1)+(1ll<<w);
}
long long js(vector<int> X,int w)
{
	if(w<0||X.empty()) return 0;
	vector<int> _1,_0;
	_1.clear(),_0.clear();
	for(int i=0,nq=X.size();i<nq;i++)
		if((X[i]>>w)&1) _1.push_back(X[i]);
		else _0.push_back(X[i]);
	long long ans=0;
	if(_1.size()&&_0.size()){
		root=cnt=0,ans=(1<<30);
		for(int i=0,n1=_1.size();i<n1;i++) addt(root,_1[i],30);
		for(int i=0,n2=_0.size();i<n2;i++) ans=min(ans,ask(root,_0[i],30));
	}
	return ans+js(_1,w-1)+js(_0,w-1);
}
int main()
{
	cin>>n;
	for(int i=1,u,v,w;i<n;i++) cin>>u>>v>>w,adde(u+1,v+1,w),adde(v+1,u+1,w),X.push_back(0);
	X.push_back(0),dfs(1,0),cout<<js(X,30);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值