【题解】CF1324F

Question

题目大意:每个点不是黑点就是白点,求以每一个点为根时,选择出一个联通块,使得白点数与黑点数之差最大(白减黑)。

\(Solution\)

考虑先跑一遍\(dp\).

可以写出一个比较显然的方程:\(dp_i=val_i+\sum_{j\in son[i]}max(0,dp_j),val_i\)是节点\(i\)的颜色,如果是白则为\(1\),否则为\(-1\).

如果这样难不成要跑\(n\)遍?

恭喜喜提\(\text{Time Limit Error}\).

考虑只跑一遍,用第一遍的答案更新其它根的答案。

考虑一个点\(i\),若已经知道\(fa_i\)的答案,怎么推出它的答案?

考虑去掉它对\(fa_i\)的影响,在加上它本身的答案。

值得注意的是,当它爹的答案本身就小于0时,就不用选了。

所以,状态转移方程为(第二遍是\(f,\)第一遍是\(dp.\)):

\[f_i=max(0,f_{fa_i}-max(dp_i,0))+dp_i \]

以上,这题\(O(n)\)做完了。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
int n,v[MAXN],head[MAXN<<1];
int tot,cnt1[MAXN],vis[MAXN];
int cnt2[MAXN],dp[MAXN],d[MAXN];
struct edge{
	int nxt,to;
}e[MAXN<<1];
inline void add(int x,int y){
	e[++tot].to=y;
	e[tot].nxt=head[x];
	head[x]=tot;
}
void dfs(int x){
	vis[x]=1;
	if(v[x])cnt1[x]=1,d[x]=1;
	else cnt2[x]=1,d[x]=-1;
	for(int i=head[x];i;i=e[i].nxt){
		int j=e[i].to;
		if(!vis[j]){
			dfs(j);
			cnt1[x]+=cnt1[j];
			cnt2[x]+=cnt2[j];
			if(d[j]<=0)continue;
			else d[x]+=d[j];
		}
	}
}
void solve(int x,int fa){
	if(x!=1)dp[x]=max(0,dp[fa]-max(0,d[x]))+d[x];
	for(int i=head[x];i;i=e[i].nxt)if(e[i].to!=fa)solve(e[i].to,x);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%d",&v[i]);
	for(int i=1,x,y;i<n;++i){
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
	dfs(1);
	dp[1]=d[1];
	for(int i=1;i<=n;++i)vis[i]=0;
	solve(1,0);
	for(int i=1;i<=n;++i)printf("%d ",dp[i]);
	cout<<endl;
	return 0;
} 

猜你喜欢

转载自www.cnblogs.com/h-lka/p/12592870.html
今日推荐