Codeforces Round #683 Div2 E Xor Tree(字典树,树上问题)

E. XOR Tree

题目描述

给出 n n n个点,每个点有一个值 a i a_i ai,对于每个点,它将找到其余所有点中与其异或和最小的点,并且与之连上双向边,问最少要在给出数组中删掉多少数才能使得剩余的数经过这样的操作能变成一颗树。

思路

同样又是熟悉的最小异或点对的问题,容易联想到CF之前的另一道XOR MST。同样首先最直接的想法是 n 2 n^2 n2的暴力求最小点对,但是显然会T飞。正解是01字典树,同样这题也是基于01字典树去思考。首先是熟悉的思路,对于某个特定01串的最小异或和串,应该是从最高位到最低位去贪心匹配(由于是二进制,所以贪心用1^1消去高位可被证明是最优解)。所以可以发现,我们从高位到低位去遍历字典树时,“1”节点的那颗子树内所存放的串一定是会相互匹配的,由此可以慢慢的可以看出树上问题的雏形。同理“0”节点内也是。然后首先,可以证明无论如何不存在一些节点他们的链可以构成一个环,由此基础上结合之前的结论,我们可以发现,只要让同一个深度上只有一个节点就能使得形成是一颗树,于是遍历一遍就可以了。

代码
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 2e5 + 5;
const int inf = 0x3f3f3f3f;
int ans = inf;
int trie[maxn << 5][2];
int cnt[maxn << 5];  //子树结点计数
int tot = 0;
void insert(int x) {
    int root = 0;
    for (int i = 30; i >= 0; --i) {
        int j = (x >> i) & 1;
        if (!trie[root][j]) {
            trie[root][j] = ++tot;
        }
        cnt[root]++;
        root = trie[root][j];
    }
}
void dfs(int u, int dep, int sum) {
    if (dep == 30) {
        ans = min(ans, sum);
        return;
    }
    int ls = trie[u][0];
    int rs = trie[u][1];
    if (!ls && !rs)
        return;
    else if (!rs) {  //不用删
        dfs(ls, dep + 1, sum);
    } else if (!ls) {
        dfs(rs, dep + 1, sum);
    } else {
        dfs(ls, dep + 1, sum + cnt[rs] - 1);  //删右子树
        dfs(rs, dep + 1, sum + cnt[ls] - 1);  //删左子树
    }
}
int main() {
    int n, x;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &x);
        insert(x);
    }
    dfs(0, 0, 0);
    printf("%d\n", ans);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值