计数题 题目大意 解题思路

给你一个完全图,每个节点的权值为a[i],两点之间连边权值为a[i] xor b[i]求最小生成树权值及个数
显然可以根据二进制位是0或1来分治

解题思路

把最高位为0和最高位为1的分治建最小生成树,再在他们之间连一条边显然是更优的。
求最小连边可以暴力生成一棵01trie再贪心匹配
附:当分治到全为同一数时,最小生成树个数是n的n-2次方

#include<bits/stdc++.h>
using namespace std;
long long n,tot,i,j,a[100005],f[1000005],ans,sum,p,son[10000005][2];
long long mi(long long a,long long b)
{
	long long v=1;
	while(b>0)
	{
		if(b&1)v=(v*a)%p;
		a=(a*a)%p;
		b>>=1;
	}
	return v;
}
void join(long long k)
{
	long long now=0;
	for(long long i=30;i>=0;i--)
	{
		long long t=(1<<i)&k;
		if(t>0)t=1;
		if(son[now][t]>0)now=son[now][t];
		else tot++,son[now][t]=tot,now=tot,son[tot][0]=son[tot][1]=f[tot]=0;;
	}
	f[now]++;
}
long long find(long long k,long long &now)
{
	long long totl=0;
	for(long long i=30;i>=0;i--)
	{
		long long t=(1<<i)&k;
		if(t>0)t=1;
		if(son[now][t]>0)now=son[now][t];
		else totl+=(1<<i),now=son[now][t^1];
	}
	return totl;
}
long long dg(long long l,long long r,long long k)
{
	long long mid,i,j,z,x,y,sum,v;
	if(l>=r)return 1;
	if(k<0)
	{
		sum=mi(r-l+1,r-l-1);
		return sum;
	}
	z=1<<k;mid=r+1;
	for(i=l;i<=r;i++)
	if((a[i]&z)>0)
	{
		mid=i;break;
	}sum=1;mid--;
	x=dg(l,mid,k-1);y=dg(mid+1,r,k-1);
	sum=(x*y)%p;
	if(mid!=0&&mid!=r)
	{
		tot=0;son[0][0]=son[0][1]=f[0]=0;
		for(i=l;i<=mid;i++)join(a[i]);
		x=214748364700;y=1;
		if(tot>0)
		for(i=mid+1;i<=r;i++)
		{
			v=0;
			z=find(a[i],v);
			if(z<x)x=z,y=f[v];
			else if(z==x)y=(y+f[v])%p;
		}
		if(x!=214748364700)ans+=x;
		sum=(sum*y)%p;
	}
	return sum;
}
int main()
{
	freopen("jst.in","r",stdin);
	freopen("jst.out","w",stdout);
	scanf("%lld",&n);p=1e9+7;
	for(i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
	sort(a+1,a+n+1);
	sum=dg(1,n,30);
	printf("%lld
%lld",ans,sum);
}