换根dp CF div2 Round 899 Tree Xor

题目链接:[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)\)

每个测试用例的第二行包含 \(n 个整数 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;
}
posted @ 2025-01-10 15:24  书面  阅读(27)  评论(0)    收藏  举报