CF1187E Tree Painting 题解

CF1187E Tree Painting 题解

原题面

前置知识: 换根(DP)

换根(DP)模板题

如果您还不会换根(DP)的话,可以先去看看UM巨佬的日报:

#278[UltiMadow] [学习笔记]换根dp

大致题意

给定一棵n个点的树 初始全是白点

要求你做n步操作,每一次选定一个与一个黑点相隔一条边的白点,将它染成黑点,然后获得该白点被染色前所在的白色联通块大小的权值。

求可获得的最大权值

分析

几乎是一道裸的模板题了...

和P3478几乎一摸一样,只是需要一个微小的结论

PS:图中节点的编号有一点微小的错误,不过并不影响阅读

假如说我们选了图中的1号节点作为第一个涂色的点(图中蓝色的点)

那下一个涂色的节点肯定就能选择它的几个儿子了(图中深红色的点)

同时,由于父亲节点已经被涂色了,其子节点不可能再和上面的"祖先"辈节点有联通了
能对其产生贡献的只有自己的子树

因此当一个父亲节点被涂色后,其所有子树都是相对"独立"的,涂色顺序的变化对总贡献值无任何影响

故当第一个节点被涂色后,剩下节点的涂色顺序均无法对总贡献值产生影响

代码实现

单纯的暴力枚举每个根的位置的话照这个数据范围肯定会T飞

考虑换根DP

应该很容易状态转移方程推出:

  • (dp[v] = dp[u]-2size[v]+size[1])

具体这个方程怎么来的,我之前写的P3478的题解跟前面UM巨佬的日报里也有讲

套上换根(DP)的板子即可

贴上丑陋的代码:(其实只要把P3478的代码改一行就可以了)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200010;
vector <int> son[MAXN];
int vis[MAXN],n;
long long size[MAXN];
long long f[MAXN];
void dfs(int u){
	size[u] = 1;
	vis[u] = 1;
	for(int i=0;i<son[u].size();i++){
		int v = son[u][i];
		if(!vis[v]){
			dfs(v);
			size[u]+=size[v];
		}
	}
}
void dp(int u){
	vis[u] = 1;
	for(int i=0;i<son[u].size();i++){
		int v = son[u][i];
		if(!vis[v]){
			f[v] = f[u] + size[1] - 2*size[v];
			dp(v);
		}
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		son[u].push_back(v);
		son[v].push_back(u);
	}
	dfs(1);
	for(int i=1;i<=n;i++){
		f[1]+=size[i];
	}
   memset(vis,0,sizeof(vis));
   dp(1);
   long long ans = -0x3f;
   for(int i=1;i<=n;i++){
   	 ans = max(ans , f[i]);
   }
   printf("%lld",ans);
}
原文地址:https://www.cnblogs.com/xcxc82/p/13429869.html