2020CCPC長春 F. Strange Memory(樹上啟發式合併)
阿新 • • 發佈:2021-11-11
Once there was a rooted tree. The tree contained n nodes, which were numbered 1,…,n. The node numbered 1 was the root of the tree. Besides, every node i was assigned a number a**i. Your were surprised to find that there were several pairs of nodes (i,j) satisfying a**i⊕a**j=alca(i,j),
where ⊕ denotes the bitwise XOR operation, and lca(i
Unfortunately, you cannot remember all such pairs, and only remember the sum of i⊕j for all different pairs of nodes (i,j) satisfying the above property. Note that (i,j) and (j
You are assumed to calculate it now in order to memorize it better in the future.
首先暴力做法就是雙重遍歷點求LCA然後判斷,複雜度顯然爆炸,考慮如何優化。既然列舉點不可,那麼可以考慮列舉LCA,求LCA為根的子樹對於答案的貢獻,加起來就是最終的答案了。這樣就自然想到了dsu on tree。由於異或的性質,可知要滿足的條件\(a[x]\ xor\ a[y] = a[LCA]\)
#include <iostream>
#include <cstring>
#define ll long long
const int N = 1e5 + 5;
using namespace std;
int n, a[N], head[N], ver[2 * N], Next[2 * N], tot = 0, sz[N], son[N];
long long ans = 0;
int hson = 0;
int cnt[N * 15][21][2];//第一維一定要開夠
void add(int x, int y) {
ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
}
void dfs1(int x, int pre) {
sz[x] = 1;
int mxsz = -1;
for(int i = head[x]; i; i = Next[i]) {
int y = ver[i];
if(y == pre) continue;
dfs1(y, x);
sz[x] += sz[y];
if(sz[y] > mxsz) {
son[x] = y;
mxsz = sz[y];
}
}
}
void update(int x, int pre, int lca) {
int tmp = a[x] ^ a[lca];
for(int i = 0; i <= 20; i++) {
ans += (1ll << i) * (cnt[tmp][i][!((x >> i) & 1)]);
}
for(int i = head[x]; i; i = Next[i]) {
int y = ver[i];
if(y == pre || y == hson) continue;
update(y, x, lca);
}
}
void modify(int x, int pre, int v) {
for(int i = 0; i <= 20; i++) {
cnt[a[x]][i][(x >> i) & 1] += v;//x的資訊必須在這裡新增才不會產生影響
}
for(int i = head[x]; i; i = Next[i]) {
int y = ver[i];
if(y == pre || y == hson) continue;
modify(y, x, v);
}
}
void dfs2(int x, int pre, bool keep) {
for(int i = head[x]; i; i = Next[i]) {
int y = ver[i];
if(y == pre || y == son[x]) continue;
dfs2(y, x, 0);
}
if(son[x]) {
dfs2(son[x], x, 1);
hson = son[x];
}
for(int i = head[x]; i; i = Next[i]) {//統計以a[x]為lca的答案
int y = ver[i];
if(y == pre || y == son[x]) continue;//因為重兒子已經add過一遍了,不能再添加了
//必須先計算再新增 否則可能出現v在u到假設的lca的鏈上,這樣假設的lca就不是真正的lca了
update(y, x, x);
modify(y, x, 1);
}
for(int i = 0; i <= 20; i++) {
cnt[a[x]][i][(x >> i) & 1]++;//x的資訊必須在這裡新增才不會產生影響
}
hson = 0;
if(!keep) {
modify(x, pre, -1);
}
}
signed main() {
cin >> n;
memset(cnt, 0, sizeof(cnt));
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
for(int i = 1; i <= n - 1; i++) {
int u, v;
cin >> u >> v;
add(u, v);
add(v, u);
}
dfs1(1, 0);
dfs2(1, 0, 1);
cout << ans;
return 0;
}