Codeforces 1285D(01字典树求异或最大值)

D. Dr. Evil Underscores

题目传送门:

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;
}

猜你喜欢

转载自blog.csdn.net/Stevenwuxu/article/details/109692791