Trie 2
trie 树上二分与按位贪心
前置芝士 Trie 1。
CF1055F Tree and XOR
树上路径的异或和显然可以套路转化。记
这题根本不用建树,由于第一个编号比第二个小。直接如下:
for (int i = 2; i <= n; i++){
int fa,w;
cin >> fa >> w;
a[i] = a[fa]^w;
}
我们很轻松能想到一个做法,先将所有数放进
这样的复杂度是
具体实现的话可以每个数都维护一个指针,即目前跳到了
int ans = 0;
for (int i = 1; i <= n; i++) p[i] = 1;
for (int i = 62; i >= 0; i--){
int cnt = 0;
//此位取零的方案数
for (int j = 1; j <= n; j++){//暴力扫一遍每一个数
int c = a[j]>>i&1; // 当前这位是什么
cnt += sz[trie[p[j]][c]];//sz 在建 trie 的时候维护即可
}
int pan = (K > cnt);//取零还是取一
if (pan){//取一
K -= cnt;
ans += (1ll<<i);
}
for (int j = 1; j <= n; j++){
int c = a[j]>>i&1;
p[j] = trie[p[j]][c^pan]; //每个数匹配到哪了
}
}
比较坑的是,这题竟然卡内存。只能滚存一下字典树,比较容易绕晕。滚存其实只要在上面的代码上每层清空重来就行,维护
for (int i = 61; i >= 0; i--){
//此位取零的方案数
for (int j = 1; j <= tot; j++){
sz[j] = trie[j][0] = trie[j][1] = 0;
}
tot = cnt = 0;
for (int j = 1; j <= n; j++){
int c = (a[j]>>i&1); // 当前这位是什么
if (!trie[old[j]][c]){
tot++;
old[j] = trie[old[j]][c] = tot;
}
else old[j] = trie[old[j]][c];
sz[old[j]]++;
}
for (int j = 1; j <= n; j++){
int c = (a[j]>>i&1);
cnt += sz[trie[p[j]][c]];
}
int pan = (K > cnt); // 取零还是取一
if (pan){
K -= cnt;
ans += (1ll<<i);
}
for (int j = 1; j <= n; j++){
int c = (a[j]>>i&1);
p[j] = trie[p[j]][c^pan];
}
这道题还是 Trie 3 这里面问题的前置。