D. Dr. Evil Underscores
题目传送门:
题目大意:
给你n个整数,然后选择一个数X,使得 ai xor X 之后的最大值最小,求最小值。
思路:
刚开始看到这种异或最大值的字眼就本能的往字典树的方向去思考。最开始想的是用每个数和其他数求异或,然后取最小值,但是n^2的复杂度肯定会超时。
然后想了一会,当某一位二进制上全是0或者是1时,x相应的位置上就应该和它们相同,这样肯定可以使最后的答案最小。关键是这一位二进制上既有1又有0时该怎么考虑。我刚开始时的想法是当前这一位有1有0时,异或之后必为1,X当前这一位随便取一个数,直接加上即可。但这样是不行的,举个例子:
0 * * * * *
1 * * * * *
当第一位取0异或后,不管星号代表的数是什么第2个数肯定要比第一个数大,那么第一个数就没有继续讨论的必要了,第一个数后面的数字也没有产生任何的有效贡献,这样当然是错误的。
所以我们就可以想到,按照二进制从左往右遍历,如果全是0或者是1,当前位贡献+0,继续向下一位遍历,如果当前位有0有1,那么就分成两个集合,分别求解子集合,然后选择更优的那个集合后把当前位的贡献加上。这显然是一个递归的过程。
AC Code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
LL a[N],sum=0;
int tot=0;
int tries[N*30][2];
void insert(LL x)
{
int u=0;
for(int i=29;i>=0;i--)
{
int v=(x>>i)&1;
if(tries[u][v]==0) tries[u][v]=++tot;
u=tries[u][v];
}
}
LL dfs(int idx,int pos)
{
if(pos==-1) return 0;
if(tries[idx][0]==0) return dfs(tries[idx][1],pos-1);
else if(tries[idx][1]==0) return dfs(tries[idx][0],pos-1);
else return (1<<pos)+min(dfs(tries[idx][0],pos-1),dfs(tries[idx][1],pos-1));
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
insert(a[i]);
}
LL maxn=dfs(0,29);
printf("%lld\n",maxn);
//system("pause");
return 0;
}