【题解】 「联合省选2020」树 trie树合并 LOJ3303
Legend
Link \(\textrm{to LOJ}\)。
给定 \(n\ (1 \le n \le 525010)\) 个节点的有根树,根为 \(1\),边权为 \(1\),每个点有权值 \(v_i\ (1 \le v_i \le 525010)\)。
定义一个节点 \(x\) 对答案的贡献为:
\[c_x = \bigoplus \limits_{y \in \rm{subtree}} v_{y}+dist(x,y)
\]
请求出 \(\sum\limits_{i=1}^n c_i\)。
Editorial
简单来说,有这么一个想法:实现一个数据结构支持
- 合并两个集合;
- 集合所有数字 \(+1\);
- 求集合所有元素异或和。
树上的合并让人很容易想到线段树合并,但是线段树合并做不到让所有数字 \(+1\)。
其实你要这么想:为什么是 \(+1\) 而不是加任意权值?是不是因为 \(\rm{std}\) 做不了?
众所周知如果你看了这篇博客,那你应该很快意识到一个问题,\(+1\) 只导致 \(\log v\) 次进位。
所以我们直接从低位到高位建立 \(\textrm{trie}\) 树,然后 \(\textrm{trie}\) 树合并即可。
整体 \(+1\) 则是模拟进位:一直沿着 \(1\) 走,回溯时交换左右儿子并更新信息,更新只要打一个区间异或标记就行了。
复杂度 \(O(n \log v)\)。
Code
这是个 \(\textrm{trie}\) 树,但我们也不妨把它建成一棵线段树。
这题成功卡了我指针线段树的空间,所以只能尝试写数组版本啦。
我代码的整体 \(+1\) 是先换儿子再往 \(0\) 走。
#include <bits/stdc++.h>
#define LL long long
#define debug(...) fprintf(stderr ,__VA_ARGS__)
#define __FILE(x)\
freopen(#x".in" ,"r" ,stdin);\
freopen(#x".out" ,"w" ,stdout)
int read(){
char k = getchar(); int x = 0;
while(k < '0' || k > '9') k = getchar();
while(k >= '0' && k <= '9')
x = x * 10 + k - '0' ,k = getchar();
return x;
}
const int MX = 525010 + 233;
int v[MX];
int head[MX] ,tot;
struct edge{
int node ,next;
}h[MX << 1];
void addedge(int u ,int v){
h[++tot] = (edge){v ,head[u]} ,head[u] = tot;
}
LL Ans;
int cnt;
struct node{
int l ,r ,size ,__xor ,ans;
int ch[2];
#define lc tr[ch[0]]
#define rc tr[ch[1]]
void pushup();
void doxor(int v);
void pushdown();
void insert(int v ,int p);
void doit(int dep);
// #undef lc
// #undef rc
}tr[MX * 64];
void node::pushup(){
size = 0;
if(ch[0]) size += lc.size;
if(ch[1]) size += rc.size;
ans = 0;
if(ch[0]) ans ^= lc.ans;
if(ch[1]) ans ^= rc.ans;
}
void node::doxor(int v){
ans ^= (size & 1) * v;
__xor ^= v;
}
void node::pushdown(void){
if(__xor){
if(ch[0]) lc.doxor(__xor);
if(ch[1]) rc.doxor(__xor);
__xor = 0;
}
}
int newnode(int l ,int r){
node *x = &tr[++cnt];
x->l = l ,x->r = r ,x->size = x->__xor = x->ans = 0;
x->ch[0] = x->ch[1] = 0;
return cnt;
}
int merge(int x ,int y){
if(!x) return y;
if(!y) return x;
if(tr[x].l == tr[x].r){
tr[x].size += tr[y].size;
tr[x].ans ^= tr[y].ans;
}
else{
tr[x].pushdown() ,tr[y].pushdown();
tr[x].ch[0] = merge(tr[x].ch[0] ,tr[y].ch[0]);
tr[x].ch[1] = merge(tr[x].ch[1] ,tr[y].ch[1]);
tr[x].pushup();
}
return x;
}
void node::insert(int v ,int p = 0){
if(l == r){
size += 1;
ans ^= v;
return ;
}
pushdown();
int mid = (l + r) >> 1;
int where = (v >> p) & 1;
if(ch[where] == 0){
if(where == 0) ch[0] = newnode(l ,mid);
else ch[1] = newnode(mid + 1 ,r);
}
tr[ch[where]].insert(v ,p + 1);
pushup();
}
void node::doit(int dep = 0){
if(l == r) return;
pushdown();
std::swap(ch[0] ,ch[1]);
if(ch[0]){
lc.doxor(1 << dep);
lc.doit(dep + 1);
}
if(ch[1]){
rc.doxor(1 << dep);
}
pushup();
}
void DFS(int x){
tr[x].insert(v[x]);
for(int i = head[x] ,d ; i ; i = h[i].next){
DFS(d = h[i].node);
merge(x ,d);
}
// tr[x].insert(v[x]);
Ans += tr[x].ans;
// debug("Ans[%d] = %d\n" ,x ,tr[x].ans);
tr[x].doit();
}
int main(){
__FILE([省选联考2020]树);
int n = read();
for(int i = 1 ; i <= n ; ++i){
v[i] = read();
newnode(0 ,(1 << 21) - 1);
}
for(int i = 2 ; i <= n ; ++i) addedge(read() ,i);
DFS(1);
std::cout << Ans << std::endl;
return 0;
}

浙公网安备 33010602011771号