很有意思的题目,我们考虑能连边的两个数一定是在 01-Trie 上距离最近的两个点。于是我们先把所有数插入到 01-Trie 上去,然后 \(dp_u\) 考虑以 \(u\) 为根的子树中最多能留几个数,他的两个儿子内部的点只能在内部转移,你只能取一个儿子和另一个儿子的一个,也就是说我们的转移为 \(dp_u = \max(dp_{lson}, dp_{rson}) + 1\)
const int M = 30;
const int N = 2e5 + 5;
int n, a[N];
struct Trie {
int t[N * M][2], ed[N * M], dp[N * M], tot;
inline void clear(void) {
for (int i = 0; i <= tot; i++) t[i][0] = t[i][1] = ed[i] = dp[i] = 0;
tot = 0;
}
Trie(void) { clear(); }
inline void insert(int val) {
int pos = 0;
for (int i = M; i >= 0; i--) {
int b = val >> i & 1;
if (!t[pos][b]) t[pos][b] = ++tot;
pos = t[pos][b];
}
ed[pos]++;
}
inline void remove(int val) {
int pos = 0;
for (int i = M; i >= 0; i--) {
int b = val >> i & 1;
if (!t[pos][b]) break;
pos = t[pos][b];
}
if (ed[pos]) ed[pos]--;
}
inline int query_min(int val) {
int pos = 0, ret = 0;
for (int i = M; i >= 0; i--) {
int b = val >> i & 1;
if (t[pos][b]) pos = t[pos][b];
else if (t[pos][1 - b]) ret = ret | (1 << i), pos = t[pos][1 - b];
else break;
} return ret;
}
inline int query_max(int val) {
int pos = 0, ret = 0;
for (int i = M; i >= 0; i--) {
int b = val >> i & 1; b = 1 - b;
if (t[pos][b]) pos = t[pos][b];
else if (t[pos][1 - b]) ret = ret | (1 << i), pos = t[pos][1 - b];
else break;
} return ret;
}
inline void dfs(int pos) {
int l = t[pos][0], r = t[pos][1];
if (l) dfs(l), chkmax(dp[pos], dp[l]);
if (r) dfs(r), chkmax(dp[pos], dp[r]);
if (l && r) dp[pos]++;
if (ed[pos]) dp[pos] = 1;
}
} T;
signed main(void) {
read(n);
for (int i = 1; i <= n; i++) read(a[i]), T.insert(a[i]);
T.dfs(0); writeln(n - T.dp[1]);
//fwrite(pf, 1, o1 - pf, stdout);
return 0;
}
标签:Xor,val,int,Tree,pos,ret,--,CF1446C,dp
From: https://www.cnblogs.com/EternalEpic/p/18430617