mengxiaolong

 

树上差分

近年的NOIP,对于树上差分的题目时有出现:(NOIP 2015《运输计划》,NOIP2016《天天爱跑步》)。

这些题目都要知道在树上从某个点到另一个点的所有路径。

但是,暴力求解这种题目经常会TLE。

这种题目需要使用树上差分。

在讲树上差分之前,首先需要知道树的以下两个性质: 

  • 任意两个节点之间有且只有一条路径。
  • 一个节点只有一个父亲节点

所以:如果假设我们要考虑的是从u到v的路径,u与v的lca是a,那么很明显,我们可以将路径拆分成两条链,u→a和a→v。那么树上差分有两种常见形式:

  • 关于边的差分;
  • 关于节点的差分。

树上差分
树上差分有两种实现方法,第一种是转换为线性差分,第二种是利用最近公共祖先(lca)进行差分。第一种方法用于修改子树,第二种方法用于修改路径

  • 第一种差分

求出dfs序,以dfs序为基础进行差分

修改以i为根的子树的权值,在dfs序中相当于修改连续区间,可以O(1)实现

统计每个节点的权值,即求前缀和

  • 第二种差分

直接进行差分,但差分维护的是每个点到根节点的路径

修改i到根路径上的权值,只要修改d[i]即可,复杂度O(1)

修改从x到y路径上的权值,可以先求它们的lca点z,将d[x]和d[y]分别相加,再减去2*d[z]即可,复杂度O(1)

统计每个节点的权值,即dfs求子树的差分和,时间复杂度O(n)

  • 关于边(边(u,fa(u))信息记在节点x上)的差分:

将边拆成两条链之后,我们便可以像差分一样来找到路径了。

因为关于边的差分,a是不在其中的,所以考虑链u→a,则就要使d[u]++,d[a]--。

然后链v→a,也是d[v]++,d[a]--。

所以合起来便是d[u]++,d[v]++,d[a]-=2。

关于边(边(u,fa(u))信息记在节点u上)的差分:
从根节点,对于每一个节点x,都有如下的步骤:

  1. 枚举x的所有子节点u
  2. dfs所有子节点u
  3. d[x]+=d[u]
    因为每个点都只会遍历一次, 所以其时间复杂度为O(n).

image

  • 关于点的差分:

还是与和边的差分一样,对于所要求的路径,拆分成两条链。

步骤也和上面一样,但是也有一些不同,因为关于点,u与v的lca是需要包括进去的,所以要把lca包括在某一条链中,最后对d数组的操作便是d[u]++,d[v]++,d[a]--,d[father[a]]--。

其时间复杂度也是一样的O(n)。

posted on 2024-11-07 19:50  zsfzmxl  阅读(14)  评论(1编辑  收藏  举报

导航