【題解】 「聯合省選2020」樹 trie樹合併 LOJ3303
阿新 • • 發佈:2020-09-19
Legend
Link \(\textrm{to LOJ}\)。
給定 \(n\ (1 \le n \le 525010)\) 個節點的有根樹,根為 \(1\),邊權為 \(1\),每個點有權值 \(v_i\ (1 \le v_i \le 525010)\)。
定義一個節點 \(x\) 對答案的貢獻為:
\[c_i = \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}\) 樹合併即可。
複雜度 \(O(n \log v)\)。
Code
這是個 \(\textrm{trie}\) 樹,但我們也不妨把它建成一棵線段樹。
這題成功卡了我指標線段樹的空間,所以只能嘗試寫陣列版本啦。
#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; }