CF 1447E Xor Tree

题目描述

https://codeforces.com/contest/1447/problem/E

Solution

容易发现序列一定有一对重边,即所有元素对中异或和最小的那对

所以我们只需要判断是否连通即可

我们考虑首先建出 $01Trie$,然后考虑什么情况下是合法的

我们考虑对于一个点的左右儿子,如果其中两个儿子都有大于等于两个数,则说明这两部分不连通

如果一个有大于等于两个数,另一个有一个数,则正好可以连通

各有一个数也可以同理,没有数则没有影响

那么都有大于等于两个数的情况,则一定要在某一边删点,使得那一边只有一个数

每次挑最小的删即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>
#define maxn 200010
#define ll long long
#define cn const node
#define gc getchar
#define pb push_back
#define offset 200000
using namespace std;

ll read() {
ll x = 0, f = 0; char c = gc();
while (!isdigit(c)) f |= c == '-', c = gc();
while (isdigit(c)) x = x * 10 + c - '0', c = gc();
return f ? -x : x;
}

inline int max(int a, int b, int c) { return max(a, max(b, c)); }

int n, m, a[maxn];

#define ch x >> i & 1
#define lc T[i].nxt[0]
#define rc T[i].nxt[1]
struct Trie {
int nxt[2], sz;
} T[maxn * 31]; int rt = 1, top = 1;
void insert(int x) {
int now = rt;
for (int i = 31; ~i; --i) {
if (!T[now].nxt[ch]) T[now].nxt[ch] = ++top;
now = T[now].nxt[ch];
} T[now].sz = 1;
}

int ans;
int dfs(int i, int d) {
if (!i) return 0; int ls, rs;
if (d == -1) return min(2, T[i].sz);
ls = dfs(lc, d - 1); T[i].sz += T[lc].sz;
rs = dfs(rc, d - 1); T[i].sz += T[rc].sz;
if (ls == 2 && rs == 2) ans += min(T[lc].sz, T[rc].sz) - 1, T[i].sz -= min(T[lc].sz, T[rc].sz) - 1;
return min(2, ls + rs);
}

int main() {
cin >> n;
for (int i = 1; i <= n; ++i) a[i] = read(), insert(a[i]);
dfs(rt, 31); cout << ans << endl;
return 0;
}