cf888G Xor-MST 异或最小生成树

33 篇文章 0 订阅
20 篇文章 0 订阅

Description


给定n个带权点,定义两点之间边权为点权异或和,求MST
n ≤ 1 0 5 ,    a i ≤ 2 30 n\le 10^5,\; a_i\le2^{30} n105,ai230

Solution


不妨从高到低贪心,我们把最高位按01分开两半分治,跨越两半的就在trie上贪心,这样做是 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)
一开始分治结束条件漏了MLE了几发,还以为是vector清空的问题

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <math.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define copy(x,t) memcpy(x,t,sizeof(x))

typedef long long LL;
const LL INF=2147483647;
const int N=200005;

int rec[N*35][2],size[N*35],tot;
int a[N],s[N];

std:: vector <int> v1,v2;

LL ans;

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void ins(int v) {
	int x=1;
	drp(i,29,0) {
		size[x]++;
		int tar=(v>>i)&1;
		if (!rec[x][tar]) {
			rec[x][tar]=++tot;
			rec[tot][0]=rec[tot][1]=0;
			size[tot]=0;
		}
		x=rec[x][tar];
	} size[x]++;
}

LL ask(int v) {
	LL res=0; int x=1;
	drp(i,29,0) {
		int tar=(v>>i)&1;
		if (size[rec[x][tar]]) {
			x=rec[x][tar];
		} else {
			x=rec[x][!tar];
			res+=(1LL<<i);
		}
	}
	return res;
}

void solve(int l,int r,int p) {
	if (l>=r||p<0) return ;
	rep(i,l,r) if ((a[s[i]]>>p)&1) {
		v1.push_back(s[i]);
	} else v2.push_back(s[i]);
	for (int i=0;i<v1.size();++i) s[l+i]=v1[i];
	int mid=v1.size()+l;
	for (int i=0;i<v2.size();++i) s[mid+i]=v2[i];
	tot=1; rec[1][0]=rec[1][1]=0;
	rep(i,l,mid-1) ins(a[s[i]]);
	LL mn=INF;
	rep(i,mid,r) mn=std:: min(mn,ask(a[s[i]]));
	if (v1.size()&&v2.size()) ans+=mn;
	v1.clear(); v2.clear();
	solve(l,mid-1,p-1); solve(mid,r,p-1);
}

int main(void) {
	int n=read(),mx=0;
	rep(i,1,n) {
		a[i]=read();
		mx=std:: max(mx,(int)log2(a[i]));
		s[i]=i;
	}
	solve(1,n,mx);
	printf("%lld\n", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值