2020牛客暑期多校训练营(第五场)B题Graph(01字典树)

2020牛客暑期多校训练营(第五场)B题Graph(01字典树)

Graph

题意:给你一个树与每个边的权值,你可以进行任意操作,加边或删边,加边的值为其连成环的其他边的异或,删边后要保证图连通,求所有边和的最小值。

题解:对于任意两点,都可以求出其边,要么题目给你了,要么可以通过异或算出来,即给了你一个完全图,然后求一个最小生成树既可。所有点间的边显然存不下,但是存在点上就可以用O(n)的内存用异或公式求出任意边,所以我们可以把边的权值转移为点上的权值,点的权值为其前缀异或;

如题目样例:

6
0 1 1
1 2 4
1 3 3
0 4 5
0 5 2

转移为点上权值后为:0 1 5 2 5 2

然后任意两点间的边为两点的异或值

之后就是求一个xor的最小生成树既可,这边推荐一个大佬的博客,写得很好

感谢大佬

方法是在字典树上求最小生成树,将所有点权放进字典树,对于字典树每一层,其为0的一端必然要与1的一端连接(如果都有点),记录枚举为0一端的所有权值与为1一端的所有权值xor(n方超时)的最小答案,这里优化,将为0一端的所有权值进为1一端的字典树中,设第i次枚举的值为x,对于其二进制的每一位在字典树中尽量走与他相同的路既可最优,时间从n方降为nlogn,层数为30,即总复杂度为O(30 * n * logn),说的有点抽象,这一段看不太懂的话可以去看下那个大佬的博客,讲的很好。

#include<iostream>
#include<vector>
using namespace std;
#define ll long long
const ll N=200007;
ll maxn=1e18+7;
ll n,a[N],cnt,p,ans,u,v,c;
ll vis[N];
struct madoka{
	ll l;
	ll r;
}ma[30*N];
vector<ll>ho;
vector<pair<ll,ll> >us[N];
void go(ll p,ll c){
	vis[p]=1;
	a[p]=c;

	for(int i=0;i<us[p].size();i++){
		if(vis[us[p][i].first]==0){
			go(us[p][i].first,c^us[p][i].second);
		}
	}
}
void up(ll &p,ll z,ll h){
	if(!p){
		p=++cnt;
	}
	if(h==-1)return;
	if(z>=(1<<h)){
		up(ma[p].r,z-(1<<h),h-1);
	}
	else{
		up(ma[p].l,z,h-1);
	}
}
ll qry(ll z,ll p,ll h){
	if(h==-1)return 0;
	int lin=z&(1<<h);
	if(lin){
		if(ma[p].r){
			return qry(z,ma[p].r,h-1);
		}
		else{
			return (1<<h)+qry(z,ma[p].l,h-1);
		}
	}
	else{
		if(ma[p].l){
			return qry(z,ma[p].l,h-1);
		}
		else{
			return (1<<h)+qry(z,ma[p].r,h-1);
		}
	}
}
ll dfs(vector<ll>ho,ll p,ll h){
	if(p==0)return 0;
	vector<ll>lp,rp;
	ll ans=maxn;
	ll lim=(1<<h);
	for(int i=0;i<ho.size();i++){
		if(ho[i]&lim){
			rp.push_back(ho[i]);
		}
		else{
			lp.push_back(ho[i]);
		}
	}
	if(lp.size()&&rp.size()){
		for(int i=0;i<rp.size();i++){
			ans=min(ans,(1<<h)+(ll)qry(rp[i],ma[p].l,h-1));
		}
	}
	else{
		ans=0;
	}
	return ans+dfs(lp,ma[p].l,h-1)+dfs(rp,ma[p].r,h-1);
}
int main(){
	scanf("%lld",&n);
	for(int i=1;i<n;i++){
		scanf("%lld%lld%lld",&u,&v,&c);
		us[u].push_back({v,c});
		us[v].push_back({u,c});
	}
	go(0,0);
	for(int i=0;i<n;i++){
		ho.push_back(a[i]);
		up(p,a[i],30);
	}
	printf("%lld
",dfs(ho,1,30));
}

原文地址:https://www.cnblogs.com/whitelily/p/13382743.html