[codeforces 1187E] Tree Painting 换根dp

[codeforces 1187E]  Tree Painting    换根dp

总目录详见https://blog.csdn.net/mrcrack/article/details/105199636

在线测评地址https://codeforces.ml/problemset/problem/1187/E

Problem Lang Verdict Time Memory
1187E - Tree Painting GNU C++11 Accepted 109 ms 13400 KB

以上述数据进行手工模拟,暴力模拟方式如下,

sz[]对应 保存该子树的节点数, d[]对应    保存每结点子树的权值总和  即 题目所求

换根dp算法思路同https://blog.csdn.net/ccsu_cat/article/details/94397592

tmp=d[u]-d[v]-sz[v];
d[v]=d[v]+tmp+n-sz[v];

解释如下:

思路:我们发现如果我们定好了第一个,那么总权值是一个定值,那么问题转化为,枚举每一个点为涂色起点所能获得的总权值,答案是最大的那个,设d[u] 为 u 子树中,先涂 u 点所能获得的总权值,sz[u] 为 u 子树大小,那么转移方程:d[u] = d[son] + sz[u],我们先一遍dfs求出以 1 为根且为涂色起点的答案,接下来再用一个dfs进行换根dp,对于 u 的儿子 v,我们要把根转移到 v,首先断开u v 的连接:d[u] =d[u] - sz[v] -d[v],然后以 v 为根再连接:d[v] =d[v] + d[u] + n - sz[v]。

n-sz[v]说明摘自http://www.machaoqiang.cn/?p=1022

当然,肯定不是以1为根就是最优了,我们尝试换根,将u的孩子v当做根,画图可以看出以v为根的总分就会少了sz[v]这一段,因为以根节点开始染色的第一次答案肯定是n。但是又多了以u为根的子树(此时u相对于v来说是孩子),这一段肯定不能用sz[u]表示,因为一共有n个点,所以这一段可以用n-sz[v]表示。

换根dp算法模拟如下

AC代码如下

#include <cstdio>
#include <algorithm>
#define LL long long
#define maxn 200010
using namespace std;
int n,head[maxn],cnt;
LL sz[maxn],d[maxn],ans;//d[]保存每结点子树的权值总和  即 题目所求;sz[]保存该子树的节点数 
struct node{
	int to,next;
}e[maxn<<1];
void add_edge(int u,int v){//邻接表
	cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
void init(){
	int i,u,v;
	scanf("%d",&n);
	for(i=1;i<n;i++){
		scanf("%d%d",&u,&v);
		add_edge(u,v),add_edge(v,u);//无向图
	}
}
void dfs1(int u,int fa){//以1为根节点,计算sz[],d[]
	int v,b;
	sz[u]=1;//每个子树的节点数初始化为1
	for(b=head[u];b;b=e[b].next){
		v=e[b].to;//用 v 表示 子节点 
		if(v==fa)continue;//如果 u 的子节点 v 为 u 的父节点 fa;(fa已经涂色) 则跳过此子节点 
		dfs1(v,u);//求子节点的权值d[v] 和 子树节点数sz[v]
		sz[u]+=sz[v];//把每一个子树大小相加
		d[u]+=d[v];//该点子节点权值 相加 
	}
	d[u]+=sz[u];//该点权值 加  所有子节点的权值 
}
void dfs2(int u,int fa){//换根dp
	int v,b;
	LL tmp;
	ans=max(ans,d[u]);//在u节点为第一次涂色情况时的权值大小 ,取大值更新ans
	for(b=head[u];b;b=e[b].next){//换根
		v=e[b].to;
		if(v==fa)continue;//父节点已经算过 跳过
		tmp=d[u]-d[v]-sz[v];
		d[v]=d[v]+tmp+n-sz[v];
		dfs2(v,u);//继续向子节点扫描
	}
}
int main(){
	init();
	dfs1(1,0);
	dfs2(1,0);
	printf("%lld\n",ans);
	return 0;
}
发布了660 篇原创文章 · 获赞 562 · 访问量 48万+

猜你喜欢

转载自blog.csdn.net/mrcrack/article/details/105199290