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

博客讲述了如何解决一道关于寻找异或操作后最大值最小问题的算法题。作者首先分析了朴素解法的不足,然后提出了利用字典树(Trie)和动态规划的思想来优化解决方案。通过遍历二进制位,将数字分为两组并分别求解,最终选择最优解。代码实现中展示了如何构建字典树并进行深度优先搜索以找到最小异或最大值。
摘要由CSDN通过智能技术生成

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值