浅谈树上差分
本篇随笔简单讲解一下信息学奥林匹克竞赛中树上差分的相关知识点。树上差分近几年成为了考试热门,也成为了考察差分思想比较常用的手段。理解树上差分最好需要读者了解图和树的基础知识,LCA及LCA问题的求法,以及差分数组和差分思想。其中最为重要的是差分思想,对于这个思想,有还不是很了解的小伙伴可以参考这篇博客:
树上差分:边的差分
我们对差分和差分数组的理解一般都是停留在数组这种线性结构上的。这可能会导致我们学习树上差分的时候无法理解这个概念。
树上差分一般应用于我们在树上的路径统计。
对于一棵树,我们可以设置差分数组(cnt[i])表示经过边(i)(边(i)的定义是链接(i)到(i)的父亲这一条边)的次数。
那么类比于差分数组,如果要从(u)到(v)都加上一个数。那么就会有(cnt[u]++,cnt[v]++,cnt[lca]-=2)。((lca)是(u,v)的最近公共祖先)
这是为什么呢?
首先,介绍树的两个性质:
-
1、任意两点之间有且只有一条简单路径。
-
2、如果是一棵无根树,那么确定根节点后,除根节点外每个点有且只有一个父亲节点。
那么,先来看一张图:
对于这棵树来讲,如果我们想对一条路径:(5-9)进行差分修改,那么从 $ 5-9 $ 的唯一简单路径就是(5-2-1-4-3-9),必过它们的最近公共祖先(1)。那么,在这条路径上 ,除了根节点之外的所有节点,因为只有一个父亲,那么这条路径既然经过它,则一定经过它的父亲。那么,根据这条性质:我们就可以把(5-9)拆成两条链(5-1)、(1-9).
那我们就可以把它变成一个差分数组的问题,对于一条链来讲,我们认为这是一个线性序列,则有:(cnt[u]++,cnt[lca]--) ,同理,有(cnt[lca]--,cnt[v]++),则推出了前面的性质。
树上差分:点的差分
对于图上的问题,有一些问题是带边权的,有一些问题则是带点权的。对于这些问题,我们可以把两种权值互相转化,即边权转点权、点权转边权等操作。但是其实有的时候完全没必要。我们完全可以可以再开发出点的差分。
类比边的差分,点的差分数组比较好定义:(cnt[i])表示(i)点一共被经过多少次。那么对一次路径修改:(u-v),就有(cnt[u]++,cnt[v]++,cnt[lca]--,cnt[fa[lca]]--)。
也就是说,比边的差分不仅减小了LCA的差分点权,还减小了其LCA父节点的差分点权。
这是为什么呢?
放原图:
还是刚刚的例子:修改(5-9)这一条路径上所有点的点权。我们需要注意的是,既然是修改点权,那么这条路径所包括的(LCA)点也需要被记录下来。那就导致了我们拆两条链 ,需要有一条链包括(LCA)点。那么再次类比差分数组,那条包括(LCA)的链就有(cnt[u]++,cnt[fa[lca]]--),不包括的就是:(cnt[v]++,cnt[lca]--)。综上,则有上面的性质。