F. Maximum White Subtree(树形dp+换根dp)

https://codeforces.com/problemset/problem/1324/F

题目描述

You are given a tree consisting of nn vertices. A tree is a connected undirected graph with n-1n−1 edges. Each vertex vv of this tree has a color assigned to it ( a_v = 1av​=1 if the vertex vv is white and 00 if the vertex vv is black).

You have to solve the following problem for each vertex vv : what is the maximum difference between the number of white and the number of black vertices you can obtain if you choose some subtree of the given tree that contains the vertex vv ? The subtree of the tree is the connected subgraph of the given tree. More formally, if you choose the subtree that contains cnt_wcntw​ white vertices and cnt_bcntb​ black vertices, you have to maximize cnt_w - cnt_bcntw​−cntb​ .

输入格式

The first line of the input contains one integer nn ( 2 \le n \le 2 \cdot 10^52≤n≤2⋅105 ) — the number of vertices in the tree.

The second line of the input contains nn integers a_1, a_2, \dots, a_na1​,a2​,…,an​ ( 0 \le a_i \le 10≤ai​≤1 ), where a_iai​ is the color of the ii -th vertex.

Each of the next n-1n−1 lines describes an edge of the tree. Edge ii is denoted by two integers u_iui​ and v_ivi​ , the labels of vertices it connects (1 \le u_i, v_i \le n, u_i \ne v_i(1≤ui​,vi​≤n,ui​​=vi​ ).

It is guaranteed that the given edges form a tree.

输出格式

Print nn integers res_1, res_2, \dots, res_nres1​,res2​,…,resn​ , where res_iresi​ is the maximum possible difference between the number of white and black vertices in some subtree that contains the vertex ii .

题意翻译

  • 给定一棵 nn 个节点无根树,每个节点 uu 有一个颜色 a_uau​,若 a_uau​ 为 00 则 uu 是黑点,若 a_uau​ 为 11 则 uu 是白点。
  • 对于每个节点 uu,选出一个包含 uu 的连通子图,设子图中白点个数为 cnt_1cnt1​,黑点个数为 cnt_2cnt2​,请最大化 cnt_1 - cnt_2cnt1​−cnt2​。并输出这个值。
  • 1 \leq n \leq 2 \times 10^51≤n≤2×105,0 \leq a_u \leq 10≤au​≤1。

输入输出样例

输入 #1复制

9
0 1 1 1 0 0 0 0 1
1 2
1 3
3 4
3 5
2 6
4 7
6 8
5 9

输出 #1复制

2 2 2 2 2 1 1 0 2

输入 #2复制

4
0 0 1 0
1 2
1 3
1 4

输出 #2复制

0 -1 1 -1

说明/提示

The first example is shown below:

The black vertices have bold borders.

In the second example, the best subtree for vertices 2, 32,3 and 44 are vertices 2, 32,3 and 44 correspondingly. And the best subtree for the vertex 11 is the subtree consisting of vertices 11 and 33 .


思路:题目给的是无根树并且求包含i节点的最大差值。先由树形dp以1为根进行dfs遍历自下往上进行树形dp。

dp[i]:以i为根的子树的最大差值。

转移方程:

这时候我们获得了dp[i]即以i为根的子树的极大差值。如果直接每次都这么去做。那么复杂度是O(n^2)的,只会TLE。

这时候需要换根dp:换根dp是用来解决不定根的问题,给一个树形结构需要对每个点作为根进行一系列统计的时候需要换根dp。

现在我们定义f[i]为包含i节点的极大差值。

图源:https://blog.csdn.net/weixin_42431507/article/details/105382359

在这里插入图片描述

假设现在需要计算包含5号节点的最大差值。那么我们可以把这棵树分成两个部分:

在这里插入图片描述

此时计算f[5]是多少。首先f[5]=dp[5]。包含5节点的最大差值有dp[5](以5为根节点的子树的最大差值的贡献)

然后考虑求x部分。首先并不是简单的dp[1]-dp[5]。因为dp[5]不一定对dp[1]有贡献。

比如:2和5节点之间有100个值为0的,那么y部分就算全部是1也不可能贡献进dp[1].

那么我们算x部分的时候,应该从y部分的根节点考虑。也就是从5号节点考虑。我们只能知道5号节点能不能给他的父亲2号节点贡献。

根据f[]的定义。f[5]=dp[5]+max(0,f[2]-max(0,dp[5]));

考虑dp[5]是否对f[2]有贡献,如果有贡献,那么f[2]-dp[5]是x部分的和。如果没有贡献,那么就是f[2]。

那么第二次f[]的转移是从父亲到儿子的,dfs过程是从父亲到儿子,且初始的时候dp[1]=f[1]。

代码如下:

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=2e5+100;
typedef long long LL;
vector<LL>g[maxn];
LL a[maxn],dp[maxn],f[maxn];
void dfs1(LL u,LL fa)
{
	if(a[u]==1) dp[u]=1;
	else if(a[u]==0) dp[u]=-1; 
	for(LL i=0;i<g[u].size();i++){
		LL v=g[u][i];
		if(fa==v) continue;
		dfs1(v,u);
		dp[u]+=max(dp[v],(LL)0);
	}
}
void dfs2(LL u,LL fa)
{
	for(LL i=0;i<g[u].size();i++)
	{
		LL v=g[u][i];
		if(v==fa) continue;
        f[v]=dp[v]+max(f[u]-max(dp[v],(LL)0),(LL)0);   
		dfs2(v,u);	
	}
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  LL n;cin>>n;
  for(LL i=1;i<=n;i++){
  	cin>>a[i];
  }
  for(LL i=1;i<n;i++){
  	LL x,y;cin>>x>>y;
  	g[x].push_back(y);g[y].push_back(x);
  }
  dfs1(1,0);
  f[1]=dp[1];
  dfs2(1,0);
  for(LL i=1;i<=n;i++) cout<<f[i]<<" ";
  cout<<endl;
return 0;
}


当然,由于dp[1]=f[1],那么第二次转移的时候直接用dp[]数组进行转移也是一样的,从前往后转移相当于清空其他的再初始化dp[1]=f[1].节省一个数组[maxn]的空间。

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=2e5+100;
typedef long long LL;
vector<LL>g[maxn];
LL a[maxn],dp[maxn];
void dfs1(LL u,LL fa)
{
	if(a[u]==1) dp[u]=1;
	else if(a[u]==0) dp[u]=-1; 
	for(LL i=0;i<g[u].size();i++){
		LL v=g[u][i];
		if(fa==v) continue;
		dfs1(v,u);
		dp[u]+=max(dp[v],(LL)0);
	}
}
void dfs2(LL u,LL fa)
{
	for(LL i=0;i<g[u].size();i++)
	{
		LL v=g[u][i];
		if(v==fa) continue;
        dp[v]=dp[v]+max(dp[u]-max(dp[v],(LL)0),(LL)0);   
		dfs2(v,u);	
	}
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  LL n;cin>>n;
  for(LL i=1;i<=n;i++){
  	cin>>a[i];
  }
  for(LL i=1;i<n;i++){
  	LL x,y;cin>>x>>y;
  	g[x].push_back(y);g[y].push_back(x);
  }
  dfs1(1,0);
  dfs2(1,0);
  for(LL i=1;i<=n;i++) cout<<dp[i]<<" ";
  cout<<endl;
return 0;
}

参考博客:

https://blog.csdn.net/weixin_42431507/article/details/105382359

https://blog.csdn.net/qq_43690454/article/details/104861014

https://wnjxyk.tech/2514.html

猜你喜欢

转载自blog.csdn.net/zstuyyyyccccbbbb/article/details/108361614
今日推荐