D. Dr. Evil Underscores

题意:今天,作为一个友谊礼物,Bakry给予Badawy n个整数a1,a2,...,an,让他去寻找一个数X,使得\(\underset{1 \leq i \leq n}{\max} (a_i \oplus X)\)最小。

输入:
第一行是一个整数n(1 <= n <= 10^5)
第二行是n个整数a1,a2,...,an(0 <= ai <= 2^30 - 1)

输出:
\(\underset{1 \leq i \leq n}{\max} (a_i \oplus X)\)的最小值

分析:对于异或最大值,我们可以采用字典树存储每个整数的01串,如果我们要去找一个数X,去查询和每个数的异或最大值,然后比较所有的异或最大值,枚举所有的x,这样会导致超时。我们可以去寻找一种更加棒的方法,当我们去遍历一颗trie树的时候,从高往低遍历每一位,每个结点都有1~2个分支,如果只有一个分支,我们用x异或的时候,我们贪心地希望x这位和这个结点的值相等,这样,我们所能得到的异或最大值会最小,如果有两个分支怎么办,我们去递归求解这两个分支,取一个min,就能得到最小值,同时,我们可以发现,我们可以在递归的时候,顺便把这个最大值求解出来,当只有一个分支的时候,意味着我们要去选择一个x,它的这位是和这个分支的位相等,意味着值为0,如果有两个分支,因为我们求解的是最大值,所以,我们会加上1 << k,这样,我们就可以得到最大值最小了。

#include 
#include 
#include 
#include 
#include 
 
using namespace std;
using LL = long long;
const int N = 3200005;
const int M = 100005;
 
int trie[N][2], idx;
int a[M];
 
void insert(int x)
{
    int p = 0;
    for (int i = 29; i >= 0; --i)
    {
        int u = x >> i & 1;
        if (!trie[p][u]) trie[p][u] = ++idx;
        p = trie[p][u];
    }
}
 
int solve(int cur, int k)
{
    if (k == -1)
        return 0;
    if (trie[cur][0] == 0)
        return solve(trie[cur][1], k - 1);
    else if (trie[cur][1] == 0)
        return solve(trie[cur][0], k - 1);
    else
    {
        return (1 << k) + min(solve(trie[cur][1], k - 1), solve(trie[cur][0], k - 1));
    }
 
}
 
int main()
{
    int n;
    scanf("%d", &n);
 
    for (int i = 1; i <= n; ++i)
    {
        scanf("%d", &a[i]);
        insert(a[i]);
    }
 
    int ans = solve(0, 29);
 
    cout << ans << endl;
        
    return 0;
}

你可能感兴趣的:(D. Dr. Evil Underscores)