文章目录
dfs序在算法在的运用
1.算法分析
1.1 基本概述
上面的图片dfs序列为{1, 2, 4, 3, 5, 6}
性质:
-
dfs的顺序不唯一,但是不影响使用。
-
子树在dfs序上一定连续
1.2 几类经典问题
1.2.1 单点修改,求子树和
问题描述:
两个操作:
1 a x
,表示将结点a的权值增加 ;2 a
,表示求结点a的子树上所有结点的权值之和。
处理方法:
求完dfs序后,在dfs序上建立树状数组(线段树)。1 a x
就是单点修改,用树状数组(线段树)进行单点修改;对于2 a
求子树和,由于子树的dfs序是连续的,因此就是进行区间查询,查询 d f n [ x ] ∼ d f n [ e d [ x ] ] dfn[x] \sim dfn[ed[x]] dfn[x]∼dfn[ed[x]]这段区间的和。
1.2.2 子树增加,子树查询
问题描述
1 a x
,表示将结点 的子树上所有结点的权值增加 ;2 a
,表示求结点 a 的权值。
处理方法:
求完dfs序后,在dfs序上建立树状数组。对于子树增加,那么就是区间修改;对于结点的权值,就是单点求值。
1.2.3 子树增加,子树查询
问题描述
1 a x
,表示将结点 a 的子树上所有结点的权值增加 ;2 a
,表示求结点 a 的子树上所有结点的权值之和。
处理方法:
求完dfs序后,在dfs序上建立树状数组。对于子树增加,那么就是区间修改;对于子树求和,那么就是区间询问。
1.2.4 树链修改,单点查询
问题描述:
1 a b x
,表示将「结点 a 到结点 b 的简单路径」上所有结点的权值都增加 ;2 a
,表示求结点 a 的权值。
处理方法:
求完dfs序后,在dfs序上建立树状数组。对于树链修改, f [ ] f[] f[]是差分数组,那么采取树上差分操作: f [ a ] + = x , f [ b ] + = x , f [ l c a ( a , b ) ] − = x , f [ f a [ l c a ( a , b ) ] ] − = x f[a] += x, f[b] += x, f[lca(a, b)] -= x, f[fa[lca(a, b)]] -= x f[a]+=x,f[b]+=x,f[lca(a,b)]−=x,f[fa[lca(a,b)]]−=x, 那么转化为4个单点修改: a d d ( b 1 , d f n [ a ] , x ) , a d d ( b 1 , d f n [ b ] , x ) , a d d ( b 1 , d f n [ l c a ( a , b ) , − x ] ) , a d d ( b 1 , d f n [ f a [ l c a ( a , b ) ] ] , − x ) add(b1, dfn[a], x), add(b1, dfn[b], x), add(b1, dfn[lca(a, b), -x]), add(b1, dfn[fa[lca(a, b)]], -x) add(b1,dfn[a],x),add(b1,dfn[b],x),add(b1,dfn[lca(a,b),−x]),add(b1,dfn[fa[lca(a,b)]],−x);对于求节点a的权值,由于a的权值等于子树和的差分值相加(树上差分原理),但是子树的dfs序连续,那么相当于区间查询(求和)。
1.2.5 树链修改,子树和查询
问题描述:
1 a b x
,表示将「结点 a 到结点 b 的简单路径」上所有结点的权值都增加 ;2 a
,表示求 的子树上所有结点的权值之和.
处理方法:
求完dfs序后,在dfs序上建立树状数组。对于树链修改, f [ ] f[] f[]是差分数组,那么采取树上差分操作: f [ a ] + = x , f [ b ] + = x , f [ l c a ( a , b ) ] − = x , f [ f a [ l c a ( a , b ) ] ] − = x f[a] += x, f[b] += x, f[lca(a, b)] -= x, f[fa[lca(a, b)]] -= x f[a]+=x,f[b]+=x,f[lca(a,b)]−=x,f[fa[lca(a,b)]]−=x。那么转化为4个单点修改: a d d ( b 1 , d f n [ a ] , x ) , a d d ( b 1 , d f n [ b ] , x ) , a d d ( b 1 , d f n [ l c a ( a , b ) , − x ] ) , a d d ( b 1 , d f n [ f a [ l c a ( a , b ) ] ] , − x ) add(b1, dfn[a], x), add(b1, dfn[b], x), add(b1, dfn[lca(a, b), -x]), add(b1, dfn[fa[lca(a, b)]], -x) add(b1,dfn[a],x),add(b1,dfn[b],x),add(b1,dfn[lca(a,b),−x]),add(b1,dfn[fa[lca(a,b)]],−x);
对于查询子树和,子树为a,对于子树a内的每个点x,子树和就是每个点x的贡献值相加。每个点x的贡献值为: f [ x ] ∗ ( d e e p [ x ] − d e e p [ a ] + 1 ) = f [ x ] ∗ ( d e e p [ x ] + 1 ) − f [ x ] ∗ d e e p [ a ] f[x] * (deep[x] - deep[a] + 1) = f[x] * (deep[x] + 1) - f[x] * deep[a] f[x]∗(deep[x]−deep[a]+1)=f[x]∗(deep[x]+1)−f[x]∗deep[a],其中 d e e p [ x ] deep[x] deep[x]表示x的深度,根节点的深度为1。按照差分思想,当前x节点加 f [ x ] f[x] f[x],那么它所以的父节点、祖父节点…都需要加上 f [ x ] f[x] f[x],因此对于子树和来说,需要加上 d e e p [ x ] − d e e p [ a ] + 1 deep[x] - deep[a] + 1 deep[x]−deep[a]+1个 f [ x ] f[x] f[x]。
按照上述分析,需要维护两个数组数组,一个维护 ∑ x = l x = r f [ x ] ∗ ( d e e p [ x ] + 1 ) \sum_{x=l}^{x=r}f[x] * (deep[x] + 1) ∑x=lx=rf[x]∗(deep[x]+