AtCoder Beginner Contest 281 F - Xor Minimization (字典树)

每周至少五篇博客:(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_iaixxx 的比特 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(dep1,treep,0)
  • 对于当前节点只有 111 边连向节点(对标上文情况 222 ),那么答案是 query(dep−1,treep,1)query(dep - 1, tree_{p,1})query(dep1,treep,1)
  • 对于当前节点既有 000 边又有 111 边连向节点(对标上文情况 333),那么取最小情况 min⁡(query(dep−1,treep,0/1))\min(query(dep-1, tree_{p, 0/1}))min(query(dep1,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(dep1,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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值