Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

CF888G Xor-MST

LuoGu: CF888G Xor-MST

CF: G. Xor-MST

考虑 Boruvka 。每次对于一个连通块找到一个连通块外的异或最小的一个数。考虑用 Trie 维护和查询,维护一个所有数构成的 Trie ,以及每个连通块一个 Trie,每次查询时作差即可。合并时暴力合并两个 Trie,注意到总节点个数最多为 $n \log n$ ,合并时每到一个节点即删去一个节点,因此是均摊 $O(n \log n)$ 的。

具体实现时,先将所有数去重,否则在 Trie 上两个点位置相同会出现 bug 。

还发现一个问题,序列是有序的在查询时常数会比无序的小很多。

查看代码
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <cstdio>
#include <algorithm>
using namespace std;
template <class Type>
void read(Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 3) + (x << 1) + c - '0';
flag && (x = ~x + 1);
}
template <class Type>
void write(Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar(x % 10 + '0');
}
typedef long long LL;
const int N = 2e5 + 10, M = 30, K = 15e6 + 10;
int n, w[N], p[N], f[N], g[N];
int idx, rt[N], id[K], sz[K], tr[K][2];
int fa (int x)
{
return p[x] == x ? x : p[x] = fa(p[x]);
}
void insert (int p, int k, int x)
{
for (int i = M - 1; ~i; i--)
{
int &t = tr[p][k >> i & 1];
!t && (t = ++idx);
sz[p = t]++;
}
id[p] = x;
}
int merge (int x, int y)
{
if (!x || !y)
return x + y;
sz[x] = sz[tr[x][0] = merge(tr[x][0], tr[y][0])] + sz[tr[x][1] = merge(tr[x][1], tr[y][1])];
return x;
}
int query (int p, int q, int k, int &s)
{
for (int i = M - 1; ~i; i--)
{
bool t = k >> i & 1;
if (tr[p][t] && sz[tr[p][t]] > sz[tr[q][t]])
p = tr[p][t], q = tr[q][t];
else
s |= 1 << i, p = tr[p][t ^ 1], q = tr[q][t ^ 1];
}
return id[p];
}
int main ()
{
read(n);
rt[0] = ++idx;
for (int i = 1; i <= n; i++)
read(w[i]);
sort(w + 1, w + n + 1);
n = unique(w + 1, w + n + 1) - w - 1;
random_shuffle(w + 1, w + n + 1);
for (int i = 1; i <= n; i++)
{
p[i] = i;
insert(rt[0], w[i], i);
insert(rt[i] = ++idx, w[i], i);
}
LL res = 0;
for (bool flag = true; flag; )
{
flag = false;
for (int i = 1; i <= n; i++)
g[i] = 0;
for (int i = 1; i <= n; i++)
{
int a = fa(i), s = 0;
int b = fa(query(rt[0], rt[a], w[i], s));
if (a == b)
continue;
(!g[a] || s < f[a]) && (g[a] = b, f[a] = s);
(!g[b] || s < f[b]) && (g[b] = a, f[b] = s);
}
for (int i = 1; i <= n; i++)
{
int a = fa(i), b = fa(g[i]);
if (!g[i] || a == b)
continue;
flag = true;
p[b] = a;
res += f[i];
merge(rt[a], rt[b]);
}
}
write(res);
return 0;
}