换根dp,一般用来解决在无根树上,需要以每个节点为根跑一边dfs的dp问题

我们做两遍dfs

先钦定任意一个点为根

第一遍,算出\(f_i\)表示\(i\)的子树产生的答案,这里,子树指的是以我们钦定的那个点为根的有根树上的子树

这是从下向上的转移,也是一般树上dp的常规操作

第二遍,我们要算出真正的答案

这次是对于无根树中的子树,在第一遍dfs中,\(f_i\)已经包含了\(i\)所有“向下”的子树的贡献,那剩下的一棵子树是以它为根并向上连向它父亲的

设\(i\)的父亲是\(fa\)

这个子树的贡献是\(ans_{fa}\)减去节点\(i\)和它“向下”的子树对\(ans_{fa}\)的贡献(\(i\)的贡献已经在第一遍算进去了,所以这遍做的其实是以\(fa\)为根的计算)

也就是\(ans_{fa}-f_i\)

那么我们只需要将\(ans_i=f_i+ans_{fa}-f_i\)

上面那个式子看起来有点傻,但其中的-+只是代表了将某些某些节点的贡献“去掉”和“加上”的方法,具体在每个题一般是不同的

当然\(+f_i\)和\(-f_i\)也不一定就能消掉(能消掉就不用换根dp了

这一遍是从上向下转移的过程


那么我们来看这个题

给定一棵\(n\)个节点无根树,每个节点有一个颜色,黑或白,黑色记为0,白色记为1

对于每个节点\(u\),选出一个包含\(u\)的连通子图,设子图中白点个数为\(cnt_1\),黑点个数为\(cnt_2\),请最大化\(cnt_1 - cnt_2\)

 

我们再代码中将黑色记为-1,白色记为1

很显然:

\[f_i=a_i+\sum_{v\subset son_i}max(f_v,0)
\]

\(a_i\)就是\(i\)号点的颜色

那么可以根据上文的讲解得出:

\[ans_i=f_i+max(ans_{fa}-max(f_i,0),0)
\]

所以得出代码:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
int x=0,y=1;
char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
int n;
struct data{
int cnt0,cnt1,ans;
};
int ans[200006],f[200006],a[200006];
int fir[200006],nex[400006],to[400006],tot;
inline void add(int x,int y){
to[++tot]=y;
nex[tot]=fir[x];fir[x]=tot;
}
void dfs1(int x,int fa){
f[x]=a[x];
for(reg int v,i=fir[x];i;i=nex[i]){
v=to[i];
if(v==fa) continue;
dfs1(v,x);
f[x]+=std::max(0,f[v]);
}
}
void dfs2(int x,int fa){
if(x!=1) ans[x]=std::max(ans[fa]-std::max(0,f[x]),0)+f[x];
for(reg int i=fir[x];i;i=nex[i])if(to[i]!=fa) dfs2(to[i],x);
}
int main(){
n=read();
for(reg int i=1;i<=n;i++){
a[i]=read();
if(!a[i]) a[i]=-1;
}
for(reg int u,v,i=1;i<n;i++){
u=read();v=read();
add(u,v);add(v,u);
}
dfs1(1,1);
ans[1]=f[1];dfs2(1,1);
for(reg int i=1;i<=n;i++) std::printf("%d ",ans[i]);
return 0;
}

最新文章

  1. 4、android BroadcastReceiver详细用法
  2. 浅析selenium的page object模式
  3. [转]centos中wget的使用方法
  4. python-dev无法安装
  5. html文本框(input)不保存缓存记录
  6. 【设计模式六大原则6】开闭原则(Open Close Principle)
  7. [转][JAVA]定时任务之-Quartz使用篇
  8. Linux命令对应的全称解释(转)
  9. 日新进用户200W+,解密《龙之谷》手游背后的压测故事
  10. (copy)赋值构造函数的4种调用时机or方法
  11. Hibernate最佳实战
  12. Linux下常见音频格式之间的转换方法
  13. LOG4J spring与mybatis整合
  14. sql server 只读帐号设置能读取存储过程,view等内容。
  15. 模板模式(TemplateMethod)
  16. linux用户及用户组操作
  17. 二.jQuery源码解析之构建jQuery之构建函数jQuery的7种用法
  18. iOS 9音频应用播放音频之播放控制暂停停止前进后退的设置
  19. java-web 登陆功能
  20. 创建Cookie抛出异常:java.lang.IllegalArgumentException: Control character in cookie value

热门文章

  1. Java第十八天,可变参数
  2. 工作中常用的Android系统ADB命令收集
  3. MODIS系列之NDVI(MOD13Q1)三:.jdk文件配置+MRT安装
  4. 在OS X环境下MySQL启动时报错
  5. 小程序wepy2 模拟vant PasswordInput, NumberKeyboard 密码输入框控件
  6. 一个spring 基本知识的微博(怎么加载多个xml、多个property文件、aop配置、监视器)
  7. 基于 HTML5 WebGL 的 CPU 监控系统
  8. nmon 的下一代工具 njmon
  9. Problem D. Ice Cream Tower
  10. 一口气带你踩完五个 List 的大坑,真的是处处坑啊!