Codeforces Round 899 div2 D. Tree XOR

D

题意

有一颗 n n n 个节点的树,每个点有点权 a i a_i ai,现在要通过下面的操作若干次,把树上每个点的点权变成全部都一样

  • 假设以某个点为树根,选定一个点 v v v 和一个非负整数 c c c ,将节点 v v v子树上的所有点的点权变为 a i ⨁ c a_i \bigoplus c aic花费 s ⋅ c s \cdot c sc 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 afavav=0,就必须改变 a v a_v av 的值,即:在 v v v 上选择一次 c = a v ⨁ a f a v c = a_v \bigoplus a_{fa_v} c=avafav 的操作。所以这样就可以从树的底层一直往上操作,这样一定是最优的。那么答案就是: ∑ 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×(avafav)

接下来考虑换根的情况:如果先前的根是 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 (aqar)×sq,将 q q q 变为新根之后, q q q 对答案就没有贡献了,所以新的答案要减去 ( a q ⨁ a r ) × s q (a_q \bigoplus a_r) \times s_q (aqar)×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) (araq)×(nsq),要加上去。

// 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; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值