题目链接:[CF div2 Round 899 Tree Xor](Problem - D - Codeforces)
题目描述:
给定一个具有\(n\)个顶点的树,顶点从1到\(n\)标记。对于每个顶点\(i\),标有整数\(a_i\),其中\(i=1,2,…,n\).您希望通过执行一些(可能为零)咒语使所有\(a_i\)相等。假设您在某个顶点处为根。
在每个咒语中,您可以选择任意顶点\(v\)和任意非负整数\(c\)。然后对于所有在顶点\(v\)的子树中的顶点\(i\),将\(a_i\)替换为\(a_i⊕c\)。该咒语的成本为\(s⋅c\),其中\(s\)是子树中的顶点数。这里⊕⊕表示按位异或运算。
设\(m_r\)为选择顶点\(r\)作为树的根时使所有\(a_i\)相等所需的最小总成本。找出\(m_1,m_2,…,m_n\)。假设选择顶点\(r\)作为树的根。那么如果从\(i\)到\(r\)的简单路径包含\(v\),则顶点\(i\)属于\(v\)的子树。
输入:
每个测试包含多个测试用例。第一行包含测试用例的数量 \(t(1≤t≤10^4)\)。随后是测试用例的描述。
每个测试用例的第一行包含单个整数 \(n(1≤n≤2⋅10^5)\)。
每个测试用例的第二行包含 \(nn 个整数 a_1,a_2,…,a_n(0≤a_i<2^{20})\)。
接下来的\(n−1\)行中,每行包含两个整数\(u\)和\(v\)\((1≤u,v≤n)\),表示存在一条连接顶点\(u\)和\(v\)的边。
保证给定的边构成一棵树。
保证所有测试用例中\(n\)的总和不超过\(2 \cdot 10^{5}\).
输出:对于每个测试用例,将 \(m_1,m_2,…,m_n\) 每个元素在新的一行上打印出来
Example
input
2
4
3 2 1 0
1 2
2 3
2 4
1
100
output
8 6 12 10
0
解题思路:
求解每一个结点作为根节点,或者求解选一个结点作为根节点的最优情况,一般来说换根DP.
\(siz[u]: 以结点u为根的子树的结点个数\)
\(dp[u]:以u为根结点的子树上,所有结点权值变成相同数,所需要的花费\)
使得父节点\(u\)和子节点\(v\)权值相同的花费为\(siz[v]*(a[u]⊕a[v])\)
\(siz[u]=\sum siz[v]+1\)
\(dp[u]=\sum dp[v]+(a[u]⊕a[v])*siz[v]\)
第一次dfs算出以1为根时,所有结点siz[]和dp[],dp[1]就是m1
接下来考虑换根(以其他结点为根)
对于每次换根,假设将根由u换成儿子v,那么对于v的所有子节点,不需要再进行异或操作,那么代价减少siz[v]* \((a_u⊕a_v)\) ,还要将u这一子树的所有结点进行异或操作,花费增加\((a_u ⊕a_v)*(n-siz[v])\)
即\(dp[v] = (dp[u] - siz[v] * (a[u]⊕a[v])) + (siz[1] - siz[v]) * (a[u] ⊕ a[v])\)
代码
// 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>
using namespace std;
using ll=long long;
using i128=__int128;
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3fLL
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll>pll;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a*b/gcd(a,b);}
ll power(ll x,ll y,ll p){ll res=1;x=x%p;while(y>0){if(y&1)res=(1ll*res*x)%p;y>>=1;x=(1ll*x*x)%p;}return res;}
ll mod_inv(ll x,ll p){return power(x,p-2,p);}
ll ceilDiv(ll n,ll m){if(n>=0)return (n+m-1)/m;else return n/m;}
#define int long long
void solve()
{int n;cin>>n;vector<int>a(n+1),dp(n+1),siz(n+1);vector<vector<int>>adj(n+1);for(int i=1;i<=n;i++)cin>>a[i];for(int i=1;i<n;i++){int u,v;cin>>u>>v;adj[u].pb(v);adj[v].pb(u);}auto dfs=[&](auto dfs,int u,int fa)->void{siz[u]=1;for(auto v:adj[u]){if(v==fa)continue;dfs(dfs,v,u);siz[u]+=siz[v];dp[u]+=dp[v]+siz[v]*(a[u]^a[v]);}}; dfs(dfs,1,0);auto dfs2=[&](auto dfs2,int u,int fa)->void{for(auto v:adj[u]){if(v==fa)continue;dp[v] = (dp[u] - siz[v] * (a[u] ^ a[v])) + (siz[1] - siz[v]) * (a[u] ^ a[v]);dfs2(dfs2, v, u);}}; dfs2(dfs2,1,0);for(int i=1;i<=n;i++)cout<<dp[i]<<' ';cout<<endl;
}
signed main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int _=1;cin>>_;while(_--)solve();return 0;
}