这个题好像在哪里做过。。。但是翻不到
基本思想:在最高位不同的两个集合里只能有一条边相连
我们可以用trie来做,每次到一个节点,就在他的两个儿子里找xor值最小的加到答案里
若有超过2个权值相同的点时,计算方案的方法为x^(x-2),这个是完全图的生成树个数公式
无压力·真rank1
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100010
#define M 1000000007
#define LL long long
using namespace std;
int s[N<<5][2],v[N],n,m,cnt=1,c[N<<5],m1;
LL S=0,A=1,c1;
inline void pow(LL x,LL k,LL& s){
for(;k>0;x=x*x%M,k>>=1) k&1?s=s*x%M:0;
}
inline void insert(int v){
int x=1;
for(int i=29;~i;--i)
x=(s[x][(v>>i)&1]?s[x][(v>>i)&1]:s[x][(v>>i)&1]=++cnt);
++c[x];
}
void gmin(int x,int y,int d,int v=0){
if(d<0){
if(v<m1){ m1=v; c1=(LL)c[x]*c[y]%M; }
else if(v==m1) c1=(c1+(LL)c[x]*c[y]%M)%M;
}
if(s[x][0] && s[y][0]){
gmin(s[x][0],s[y][0],d-1,v);
if(s[x][1] && s[y][1]) gmin(s[x][1],s[y][1],d-1,v);
} else if(s[x][1] && s[y][1]) gmin(s[x][1],s[y][1],d-1,v);
else{
if(s[x][0]) gmin(s[x][0],s[y][1],d-1,v+(1<<d));
if(s[x][1]) gmin(s[x][1],s[y][0],d-1,v+(1<<d));
}
}
void solve(int x,int d){
if(d<0){ if(c[x]>2) pow(c[x],c[x]-2,A); return; }
if(!s[x][0]) solve(s[x][1],d-1); else
if(!s[x][1]) solve(s[x][0],d-1); else{
solve(s[x][0],d-1);
solve(s[x][1],d-1);
m1=1<<30; c1=0;
gmin(s[x][0],s[x][1],d-1);
S=S+(1ll<<d)+m1;
A=A*c1%M;
}
}
int main(){
scanf("%d",&n);
for(int x,i=1;i<=n;++i)
scanf("%d",&x),insert(x);
solve(1,29);
printf("%lld
%lld",S,A);
}