F. Maximum White Subtree(dfs+bfs)

F. Maximum White Subtree

写在前面

大多数人写的换根 d p dp dp,于是便有了这篇题解。

题目大意:

一颗树,每个点或黑或白,问所有包含这个点的链最大的 c n t 白 − c n t 黑 cnt白-cnt黑 cntcnt

思路:

一个点相连的有它的子和他的父。


可以分开考虑和子相连还是和父相连。
第一遍 d f s dfs dfs由子更新父(和子相连)

int dep[N];  // 记录深度
void dfs(int now,int last){
	if(col[now]==1) ans[now]=1;  // cnt白-cnt黑
	else ans[now]=-1;
	dep[now]=dep[last]+1;
	for(auto it:ve[now]){
		if(it!=last){
			dfs(it,now);
			if(ans[now]+ans[it]>ans[now]){  // 尝试更新
				ans[now]=ans[now]+ans[it];  
				ll s=(ll)now*(ll)1000000+(ll)it;  // 记录now->it连了边
				mp[s]=1;
			}
		}
	}
}

注意一点:
这里配合 m a p map map可以记录两点之间存在边(点的编号在 2 e 5 2e5 2e5以内这样不会有冲突)
其作用在 b f s bfs bfs上。

ll s=(ll)now*(ll)1000000+(ll)it; 

第二遍 b f s bfs bfs由父更新子(和父相连)
这里要注意一点。
1 1 1在第一遍 d f s dfs dfs中已经和 3 3 3连边,也就是说 1 1 1的答案已经包含 3 3 3故更新是

ans[it]=max(ans[it],ans[now]); // now(1), it(3)

2 2 2在第一遍 d f s dfs dfs中未和 3 3 3连边,也就是说 2 2 2的答案不包含 3 3 3。故更新是

ans[it]=max(ans[it],ans[it]+ans[now]); // now(2), it(3)


判断连边直接查 m a p map map即可。

int flag[N];
void bfs(int u){
	queue<int> p; p.push(u); flag[u]=1;
	while(!p.empty()){
		int now=p.front(); p.pop();
		for(auto it:ve[now]){
			if(dep[it]>dep[now]){  // 保证父更新子
				ll s=(ll)now*(ll)1000000+(ll)it;
				if(!mp[s]){  // 没有连边
					ans[it]=max(ans[it],ans[it]+ans[now]);
				}
				else{
					ans[it]=max(ans[it],ans[now]);
				}
			}
			if(!flag[it]){
				p.push(it);
				flag[it]=1;
			}
		}
	}
}

Code

#include <iostream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
#include <vector>
#include <string>
#include <iomanip>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
//#include <unordered_map>
#define guo312 std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define ll long long
#define Inf LONG_LONG_MAX
#define inf INT_MAX
#define endl "\n"
#define PI 3.1415926535898
using namespace std;
const int N=2e5+10;
int n,col[N],ans[N];
vector<int> ve[N];
map<ll,int> mp;

int dep[N];
void dfs(int now,int last){
	if(col[now]==1) ans[now]=1;
	else ans[now]=-1;
	dep[now]=dep[last]+1;
	for(auto it:ve[now]){
		if(it!=last){
			dfs(it,now);
			if(ans[now]+ans[it]>ans[now]){
				ans[now]=ans[now]+ans[it];
				ll s=(ll)now*(ll)1000000+(ll)it;
				mp[s]=1;
			}
		}
	}
}

int flag[N];
void bfs(int u){
	queue<int> p; p.push(u); flag[u]=1;
	while(!p.empty()){
		int now=p.front(); p.pop();
		for(auto it:ve[now]){
			if(dep[it]>dep[now]){
				ll s=(ll)now*(ll)1000000+(ll)it;
				if(!mp[s]){
					ans[it]=max(ans[it],ans[it]+ans[now]);
				}
				else{
					ans[it]=max(ans[it],ans[now]);
				}
			}
			if(!flag[it]){
				p.push(it);
				flag[it]=1;
			}
		}
	}
}

int main(){
guo312;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>col[i];
	}
	for(int i=1;i<n;i++){
		int u,v; cin>>u>>v;
		ve[u].push_back(v);
		ve[v].push_back(u);
	}
	dfs(1,0); bfs(1);
	for(int i=1;i<=n;i++){
		cout<<ans[i]<<" ";
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

要用bug来打败bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值