C. Xor Tree 字典树 + 思维

C. Xor Tree 字典树

题目大意:

给你n个数,每一个数有一个权值,对于 \(a_i\) 来说,它会找到在这个集合中和他异或值最小的一个数 \(a_j\) ,那么 \((i,j)\) 之间会连一条无向边,你可以选择删掉一些节点,使得最后剩下的节点形成一棵树,删掉的最少的节点是多少?

\(tips:\) \(a_i\) 互不相同

题解:

这个题目我是看了题解才会写的,看完题解之后感觉这个题目很简单了。。。可惜我就是没有想到。

对于异或值,而且是找异或值最小的,其实一般都是要用字典树找的,不然这么大的范围,我是无法在时限内找到这个值的。

所以首先我们尝试建一棵字典树,然后你会发现,对于节点 \(u\) 如果它左子树的叶子节点大与等于2,并且右子树的叶子节点也大于等于2,那么在这个位置会分裂成两棵树,这个是不被允许的,所以必须把一颗子树内的点删到只有一个节点。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
int trie[maxn*32][2],num[maxn*32],tot,a[maxn];
void insert(ll x){
    int u = 0;
    for(int i=31;i>=0;i--){
        int v = (x>>i)&1;
        if(trie[u][v]==0) trie[u][v] = ++tot;
        u = trie[u][v];
//        printf("u = %d i = %d v = %d x = %lld\n",u,i,v,x);
        num[u]++;
    }
}

int dfs(int u){
    int ans = 0,num = 0;
//    printf("u = %d\n",u);
    for(int i=0;i<2;i++){
        int v = trie[u][i];
//        printf("v = %d i = %d\n",v,i);
        if(v){
            num++;
            int res = dfs(v);
            ans = max(ans,res);
        }
    }
//    printf("u = %d ans = %d\n",u,ans+num>1);
    ans = max(ans+(num>1),1);
    return ans;
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),insert(a[i]);
    int ans = dfs(0);
    printf("%d\n",n-ans);
    return 0;
}
posted @ 2021-02-04 17:10  EchoZQN  阅读(90)  评论(0)    收藏  举报