题目大意
给你一个完全图,每个节点的权值为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);
}