题解【CF219D】 换根法与树形dp
我最开始也一直不太理解换根法,后来自己慢慢磨懂了,写一篇题解,加深一下印象,也希望能帮助到大家
题意简述:给你一棵树,树上的边是有向的,选一个点(中心点),反转一些边,使这个点的能达到所有点,求这个点与最小反转次数。
做法:首先很容易想到的是先跑一次遍历,遍历时统计u的子树以u为中心点有多少边需要反转
(注:存图时存双向有权边,正向边权为0即反转0次,反向为1即反转1次)
void dfs1(int u,int fa)
{
for(int i=0;i<ed[u].size();++i)
{
int v=ed[u][i].to;
if(v==fa) continue;
dfs1(v,u);
cnt[u]+=cnt[v]+ed[u][i].w;
}
}
接下来你会发现 cnt[u]只存储了以u为根节点的反转次数,而没有统计从父亲链接的带反转的边,但是有一个节点所统计的就是以这个节点作为中心点的值,
它就是根节点
(因为它没有父亲)
所以它儿子反转边之和就等于它的代价
那么就有一个点的cnt值等于所求值了
为了方便,再开一个数组f,其中f[root]=cnt[root]
如果我们知道节点u到节点v的边,节点u的代价,那么我们可以由此推出节点v的代价推理如下
对于u和v外侧的那些点,它们的目标方向没有改变,目标方向改变了的只有(u->v)变成了(v->u)
对这条边进行考虑 节点u到节点v 这条边本本身不需要反转,但我们把v节点作为中心点时,这条边就需要反转了
所以
$ for(u->v) f[u]=f[v]+1 $
$ for(v->u) f[u]=f[v]-1 $
所以我们再进行一次dfs
void dfs2(int u,int fa)
{
for(int i=0;i<ed[u].size();++i)
{
int v=ed[u][i].to;
if(v==fa) continue;
f[v]=f[u]+(ed[u][i].w?-1:1);
dfs2(v,u);
}
}
总结一下:这个换根本质就是用$O(n)$的时间复杂度求出根节点的答案,再树形dp推出整棵树的答案,就相当于拿节点 u 当根节点的答案 推出 u的儿子 son 这样推出整棵树的大答案
时间复杂度$O(n)$
本文已经结束了。本文作者:ღꦿ࿐(DeepSea),转载请注明原文链接:https://www.cnblogs.com/Dreamerkk/p/17970995,谢谢你的阅读或转载!