CF1324F题解

自己入ACM的坑也有一年多了,想想自己一年前只能做到div3的D,现在已经能把F顺利做出来了。不管以后能走多远,总想留下一些东西。
题意:给定n个结点一棵树,每个结点为黑色或白色,对于每个结点,找到包含它的一棵子树,使子树的白结点与黑结点个数之差最大。
很容易想到O(n*n)的算法,就是以每个结点为根,从上到下dfs求每颗子树的能向下延伸的最大值,然后如果子树的最大值比0大,就将父亲的ans加上它。跑n遍dfs,这样显然是会TLE的。
那么该如何优化它呢?换根DP。可以明显发觉,每个父亲的ans和他的儿子的ans之间是有关系的。我们第一遍dfs求的ans,其实已经接近最大值了,因为是树性结构,对于任一结点(除根以外,因外此时根的ans就是答案),每个儿子对它的贡献已经算出来了,只是缺少他的父亲。所以再写一个的dfs2,从根开始,如果之前的dfs将儿子的贡献算在了父亲中,那么就将父亲的贡献也加到孩子中,此时儿子的ans已是正确的,不断向下递归,最后更新完整棵树。
实现的细节可以看我的代码。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int n;
int save[200010];
vector<int> bian[200010];
int l,r;
int ans[200010];
void dfs(int x,int fa)
{
	if (save[x]==1)
	{
		ans[x]=1;
	}
	else
	{ans[x]=-1;
		
	}
	for(int i=0;i<bian[x].size();i++)
	{
		if (fa==bian[x][i])
		{
			continue;
		}
		dfs(bian[x][i],x);
		if (ans[bian[x][i]]>=0)
		{
			ans[x]+=ans[bian[x][i]];
		}
	}
 } 
 
 
 
 void dfs2(int x,int fa)
 {
 	for(int i=0;i<bian[x].size();i++)
 	{
 		if (fa==bian[x][i])
 		{
 			continue;
		 }
		 if(ans[bian[x][i]]>=0&&ans[x]-ans[bian[x][i]]>=0)
		 {
		 	ans[bian[x][i]]=ans[bian[x][i]]+ans[x]-ans[bian[x][i]];
		 }
		 else if (ans[bian[x][i]]<0&&ans[x]>=0)
		 {
		 	ans[bian[x][i]]+=ans[x];
		 }
		 dfs2(bian[x][i],x);
	 }
 }
int main()
{
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&save[i]);
	}
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&l,&r);
		bian[l].push_back(r);
		bian[r].push_back(l);
	}
	dfs(1,0);
	dfs2(1,0);
	for(int i=1;i<=n;i++)
	{
		cout<<ans[i]<<" ";
	}
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值