每周至少五篇博客:(3/5)
https://atcoder.jp/contests/abc281/tasks/abc281_f
题意
给你一个非负整数序列 A=(a1,…,aN)A=(a_1,\ldots,a_N)A=(a1,…,aN) 。
让我们只对 AAA 执行一次下面的操作。
- 选择一个非负整数 xxx 。然后,对于每一个 i=1,…,Ni=1, \ldots, Ni=1,…,N ,将 aia_iai 的值替换为 aia_iai 和 xxx 的比特 XOR。
设 MMM 为操作后 AAA 中的最大值。求 MMM 的最小可能值。
思路
这题没有用到什么性质,但是使用字典树的结构可以很好的理解这道题
考虑答案二进制中第 iii 位的 111 能否消除掉,对于第 iii 位,如果数组中所有元素二进制的第 iii 位:
- 都是 000 :那么 xxx 可以在第 iii 位构造 000,使得答案的第 iii 位是 000
- 都是 111 :那么 xxx 可以在第 iii 位构造 111,使得答案的第 iii 位是 000
- 既有 000 又有 111:那么无论 xxx 在第 iii 位构造 0/10/10/1 ,答案的第 iii 位都是 111
用字典树将 nnn 个元素的信息存储起来
定义 treeu,0/1tree_{u, 0/1}treeu,0/1 表示节点 uuu 通过 0/10/10/1 边连向的节点编号
定义 query(dep,p)query(dep, p)query(dep,p) 表示字典树深度为 depdepdep 节点编号是 ppp 时可以异或得到的最小的最大值答案
- 对于当前节点只有 000 边连向节点(对标上文情况 111 ),那么答案是 query(dep−1,treep,0)query(dep - 1, tree_{p,0})query(dep−1,treep,0)
- 对于当前节点只有 111 边连向节点(对标上文情况 222 ),那么答案是 query(dep−1,treep,1)query(dep - 1, tree_{p,1})query(dep−1,treep,1)
- 对于当前节点既有 000 边又有 111 边连向节点(对标上文情况 333),那么取最小情况 min(query(dep−1,treep,0/1))\min(query(dep-1, tree_{p, 0/1}))min(query(dep−1,treep,0/1)) ,此外还需要加上第 depdepdep 位对答案的贡献 2dep2^{dep}2dep,所以答案是 min(query(dep−1,treep,0/1))+2dep\min(query(dep-1, tree_{p, 0/1})) + 2^{dep}min(query(dep−1,treep,0/1))+2dep
代码
const int M = 30;
int tot = 0, tree[N][2], exist[N];
void insert(int x) {
int p = 0;
for (int i = M; i >= 0; i --) {
int u = x >> i & 1;
if (!tree[p][u]) tree[p][u] = ++ tot;
p = tree[p][u];
exist[p] ++;
}
}
int query(int dep, int p) {
int l = tree[p][0], r = tree[p][1];
if (exist[l] && exist[r]) return std::min(query(dep - 1, l), query(dep - 1, r)) | (1 << dep);
else if (exist[l]) return query(dep - 1, l);
else if (exist[r]) return query(dep - 1, r);
else return 0;
};
void solve() {
int n;
std::cin >> n;
for (int i = 0; i < n; i ++) {
int x;
std::cin >> x;
insert(x);
}
std::cout << query(M, 0);
}