题意
有一颗 n n n 个节点的树,每个点有点权 a i a_i ai,现在要通过下面的操作若干次,把树上每个点的点权变成全部都一样。
- 假设以某个点为树根,选定一个点 v v v 和一个非负整数 c c c ,将节点 v v v 的子树上的所有点的点权变为 a i ⨁ c a_i \bigoplus c ai⨁c ,花费是 s ⋅ c s \cdot c s⋅c , s s s 代表 v v v 的子树里节点的数量
给出分别以 n n n 个点为根的 最小花费
思路
先考虑以某个点为根的情况: 假设节点 v v v 的父亲是 f a v fa_v fav,观察发现:为了使 a f a v ⨁ a v = 0 a_{fa_v} \bigoplus a_v = 0 afav⨁av=0,就必须改变 a v a_v av 的值,即:在 v v v 上选择一次 c = a v ⨁ a f a v c = a_v \bigoplus a_{fa_v} c=av⨁afav 的操作。所以这样就可以从树的底层一直往上操作,这样一定是最优的。那么答案就是: ∑ v ≠ r o o t s v × ( a v ⨁ a f a v ) \sum_{v \neq root} s_v \times (a_v \bigoplus a_{fa_v}) ∑v=rootsv×(av⨁afav)
接下来考虑换根的情况:如果先前的根是 r r r ,现在换成了一个与它相邻的新根 q q q。那么 q q q 和 r r r 的子树对答案的贡献都不会变,唯一改变的只有 q q q 和 r r r 的贡献。以 r r r 为根时, q q q 对答案的贡献是 ( a q ⨁ a r ) × s q (a_q \bigoplus a_r) \times s_q (aq⨁ar)×sq,将 q q q 变为新根之后, q q q 对答案就没有贡献了,所以新的答案要减去 ( a q ⨁ a r ) × s q (a_q \bigoplus a_r) \times s_q (aq⨁ar)×sq。同样的道理,以 r r r 为根时, r r r 对答案没有贡献,但是换了 q q q 为根之后, r r r 对答案的贡献就是 ( a r ⨁ a q ) × ( n − s q ) (a_r \bigoplus a_q) \times (n-s_q) (ar⨁aq)×(n−sq),要加上去。
// Problem: D. Tree XOR
// Contest: Codeforces - Codeforces Round 899 (Div. 2)
// URL: https://codeforces.com/contest/1882/problem/D
// Memory Limit: 512 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
const int N=200050;
ll num[N]; //子树大小
int a[N];
std::vector<int> edge[N];
ll ans[N];
int n;
void dfs1(int u,int fa){
num[u] = 1;
for(auto v : edge[u])
if(v != fa){
dfs1(v,u);
num[u] += num[v];
}
}
void dfs2(int u,int fa){
if(u != 1) ans[1] += (a[u] ^ a[fa])*num[u];
for(auto v : edge[u])
if(v != fa)
dfs2(v,u);
}
void dfs3(int u,int fa){
if(u != 1){
ans[u] = ans[fa];
ans[u] -= (a[u] ^ a[fa])*num[u];
ans[u] += (a[u] ^ a[fa])*(n - num[u]);
}
for(auto v : edge[u])
if(v != fa)
dfs3(v,u);
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin>>t;
while(t--){
std::cin>>n;
fore(i,1,n+1) std::cin>>a[i];
fore(i,1,n+1){
ans[i] = num[i] = 0;
edge[i].clear();
}
fore(i,1,n){
int u,v;
std::cin>>u>>v;
edge[u].push_back(v);
edge[v].push_back(u);
}
dfs1(1,0); //for num[]
dfs2(1,0); //solve ans[1] first
dfs3(1,0); //root change
fore(i,1,n+1) std::cout<<ans[i]<<" \n"[i==n];
}
return 0;
}