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;
}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号