CodeForces 1285D Dr. Evil Underscores

Description CodeForces 1285D

描述

给一个长度为 $n$ 的数组 $a$,试找到一个 $X$,使得 $\max\limits_{1\le i \le n} (a_i \oplus X)$ 最小,输出这个最小值即可。

输入

第一行一个数 $n(1 \le n \le 10^5)$,接下来第二行 $n$ 个数表示 $a$ 数组 $(0\le a_i \le 2^{30} - 1)$。

输出

一行一个数,表示所求最小值。

样例

输入1

3
1 2 3

输出1

2

输入2

2
1 5

输出2

4

解释

在第一个样例中,我们可以选 $X = 3$。

在第一个样例中,我们可以选 $X = 5$。

Solution

看到异或,很自然想到从高位到低位贪心。如果较高的位置是能设为 $0$ 的,那一定将它设为 $0$,这样的答案一定最优。

我们可以对数集建 01-trie,为了适应从高位到低位的贪心,我们将第一层(也就是根节点所在的那一层)的 $dep$ 设为 $30$,从上到下依次 $-1$,得到一个总深度为 $30$ 的 trie,$dep = i$ 的层表示的是 $i$ 位的数字,位数从 $0$ 开始计算,最高到 $29$ 位,可以满足题目中 $a_i < 2^{30}$ 的要求。

比如样例,有三个数 $1, 2, 3$,我们的 01-trie 是这个样子的:

$\bullet$ 插入 $1$:

$\bullet$ 插入 $2$:

$\bullet$ 插入 $3$:

 插入的代码不难写出:

void insert(LL rt, LL val, LL dep)
{
	o[rt].dep = dep;
	if(!dep) return;
	if(!o[rt].ch[val >> dep - 1 & 1]) o[rt].ch[val >> dep - 1 & 1] = ++siz;
	insert(o[rt].ch[val >> dep - 1 & 1], val, dep - 1);
}

然后就可以在 01-trie 上 DP,在每个结点维护一个 $res$,表示以它为根的树的答案。比如现在要求 $res_{rt}$ 根据树形 DP 的基本操作,当然是先递归它的儿子,然后分类讨论一下:

  $\bullet$ 如果 $rt$ 只有一个儿子,那么如果是 $ch_0$,$X$ 的这位就设成 $0$(保持不变),否则就设为 $1$(取反),总有办法使得这个位置的答案是 $0$,所以直接返回该儿子的 $res$ 即可

  $\bullet$ 如果 $rt$ 两个儿子都有,那么如果 $X$ 这位设成 $0$,$ch_1$ 的该位就依然是 $1$;如果设为 $1$,$ch_0$ 的该位就变成了 $1$,既然 $2^{dep_{rt} - 1}$($-1$ 因为是儿子那位的答案,写成 $2 ^{dep_{son}}$ 也行)逃不掉了,在此基础上加上 $\min(res_{ch_0}, res_{ch_1})$ 再返回即可

DP 的代码:

void solve(LL rt)
{
	if(o[rt].ch[0]) solve(o[rt].ch[0]);
	if(o[rt].ch[1]) solve(o[rt].ch[1]);
	if(o[rt].ch[0] && o[rt].ch[1])
		o[rt].res = min(o[o[rt].ch[0]].res, o[o[rt].ch[1]].res) + (1LL << o[rt].dep - 1);
	else o[rt].res = o[o[rt].ch[0]].res + o[o[rt].ch[1]].res;
}

最后根节点 $1$ 的 $res$ 就是答案,完整的代码给出,供参考。

#include <bits/stdc++.h>
#define ROOT 1
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
struct node
{
	LL res, dep, ch[2];
	node() { res = dep = ch[0] = ch[1] = 0; } 
} o[N * 20];
LL a[N], n, siz = 1;
void insert(LL rt, LL val, LL dep)
{
	o[rt].dep = dep;
	if(!dep) return;
	if(!o[rt].ch[val >> dep - 1 & 1]) o[rt].ch[val >> dep - 1 & 1] = ++siz;
	insert(o[rt].ch[val >> dep - 1 & 1], val, dep - 1);
}
void solve(LL rt)
{
	if(o[rt].ch[0]) solve(o[rt].ch[0]);
	if(o[rt].ch[1]) solve(o[rt].ch[1]);
	if(o[rt].ch[0] && o[rt].ch[1])
		o[rt].res = min(o[o[rt].ch[0]].res, o[o[rt].ch[1]].res) + (1LL << o[rt].dep - 1);
	else o[rt].res = o[o[rt].ch[0]].res + o[o[rt].ch[1]].res;
}
int main()
{
	ios::sync_with_stdio(false);
	cin >> n;
	for(int i = 1; i <= n; i++)
	{
		cin >> a[i];
		insert(ROOT, a[i], 30);
	}
	solve(ROOT);
	cout << o[ROOT].res << endl;
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/syksykCCC/p/CF285D.html