题意:
给你n个数对应n个节点,对与每一个数ai,在剩余数中找一个aj,使得ai^aj异或值为最小值,并将这两个节点连无向边,由此可以得到一个连通图,可以选择删除一些数,求最少删除多少数可以使得最终连通图为一颗树
解法:
把每一个数放到01字典树上面,假设每个字数根节点的值表示为该子树中覆盖的数ai可以组成的最大连通块,
那么对于一个父节点u和它的两个儿子节点x和y,
1.如果x和y子树上都有题目给的数,u子树的最大联通块大小ans_u=max(ans_x+1,ans_y+1) 此处+1表示左子树的连通块最多只能再加一个右子树中的数,如果加两个那么这两个数连边才是最小异或,会出现两个连通块的情况不合法
2.如果x和y子树其中只有一个子树(该子树代号为z)上有题目给的数,那么ans_u=ans_z
最后dfs即可
#include<bits/stdc++.h> using namespace std; int n; int a[200005]; int tree[30*200005][2]; int tot=1; void insert(int x) { int now=1; for(int i=30;i>=0;i--) { int id =( (x>>i)&1); if(!tree[now][id]) { tree[now][id]=++tot; } now=tree[now][id]; } } int ans=0; int dfs(int u,int dep)//表示01字典树上编号为u的子树 的最大连通块大小 { if(dep==0) return 1; int ans=0; if(tree[u][0]&&tree[u][1]) { ans=max(ans,dfs(tree[u][0],dep-1)+1); ans=max(ans,dfs(tree[u][1],dep-1)+1); } else if(tree[u][0]) { ans=max(ans,dfs(tree[u][0],dep-1)); } else if(tree[u][1]) { ans=max(ans,dfs(tree[u][1],dep-1)); } return ans; } int main() { scanf("%d",&n); for(int i=0;i<n;i++) { int v; scanf("%d",&v); insert(v); } printf("%d\n",n-dfs(1,31)); }