题目大意:每个点不是黑点就是白点,求以每一个点为根时,选择出一个联通块,使得白点数与黑点数之差最大(白减黑)。
(Solution)
考虑先跑一遍(dp).
可以写出一个比较显然的方程:(dp_i=val_i+sum_{jin son[i]}max(0,dp_j),val_i)是节点(i)的颜色,如果是白则为(1),否则为(-1).
如果这样难不成要跑(n)遍?
恭喜喜提( ext{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;
}