CF1446C Xor Tree

CF1446C Xor Tree

正难则反,遇到这种求最小删除数的题目,都可以转化为求最大保留数。

题目可以转化为:保留最多的数,使得只存在一对 \((i, j)\) 对于彼此而言 \(a_i \oplus a_j\) 的值都是最小的。(也就是有且仅有一条重边)

建立 \(01trie\),记 \(f_u\) 表示以 \(u\) 为根的子树内最多可以保留多少个数。

转移分为以下三种情况:

1、\(u\) 是叶子,则 \(f_u = 1\)

2、\(u\) 只有一个儿子 \(v\),则 \(f_u = f_v\)。

3、\(u\) 有两个儿子。下面主要考虑这个怎么做。

注意到一个重要性质:由于 $01trie $ 是从高位到低位建立的,因此子树内的数之间异或一定小于子树之间异或的结果。所以如果选择保留一棵子树的数,那么另一棵子树只能保留一个数。(若保留两个以上则又会产生新的重边)

所以 \(f_u = \max(f_{v1}, f_{v2}) + 1\)。

于是 \(dfs\) 一遍就做完了,时间复杂度 \(O(n)\),数组记得开大点,像 \(trie\) 这种建议至少开 \(10\) 倍空间。

想了一下还有一个顾虑是很多个环怎么办,但其实考虑一下连边的方式,一棵子树相当于一个连通块,因为不断有另一棵子树(也就是另一个集合)的点接进来,所以不会产生环。

#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l); i <= (r); ++ i)
#define G(i,r,l) for(int i(r); i >= (l); -- i)
using namespace std;
using ll = long long;
const int N = 2e6;
int  n, cnt;
int a[N], ch[N * 2][2], f[N * 2];
void insert(int x){
	int u = 0;
	G(i, 30, 0){
		int v = ((x >> i) & 1);
		if(!ch[u][v]) ch[u][v] = ++ cnt;
		u = ch[u][v]; 
	}
}
void dfs(int u){
	int v1 = ch[u][0], v2 = ch[u][1];
	if(v1 && v2){
		dfs(v1);
		dfs(v2);
		f[u] = max(f[v1], f[v2]) + 1;
	}
	else if(v1 && !v2){
		dfs(v1);
		f[u] = f[v1];
	}
	else if(!v1 && v2){
		dfs(v2);
		f[u] = f[v2];
	}
	else{
		f[u] = 1;
	}
//	printf("u = %d\tf[%d] = %d\n", u, u, f[u]); 
	return ; 
} 
void Main(){
	cnt = 0;
	cin >> n;
//	F(i, 0, cnt){
//		f[i] = 0;
//		ch[i][0] = ch[i][1] = 0; 
//	}
	F(i, 1, n){
		cin >> a[i]; 
		insert(a[i]); 
	}
	dfs(0);
	cout << n - f[0] << '\n';
	return ;
}
signed main(){
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int T = 1;
	while(T --) Main();
	return fflush(0), 0;
}
posted @ 2026-09-05 19:48  superl61  阅读(13)  评论(0)    收藏  举报