差分,能够巧妙地维护多种区间修改,将区间修改转化为单点修改,将询问转化为前缀查询。
但是,差分能支持的,岂止是这些。有时,差分一下,性质多多。
例1. [Luogu P4552]IncDec Sequence
Description
给定一个序列,请求出至少需要多少次区间统加 1 1 1或区间统减 1 1 1的操作才能使得整个序列中的数两两相同。
为了增加难度,你也需要求出,在满足操作次数最小且最终序列中的数两两相同的前提下,最终能够得到多少种不同的序列。
Solution
本题中有区间修改这一说。不难想到差分。
现在,我们完成了一个重要的转化: 给定一个长度为 n + 1 n+1 n+1的序列,每次可以选择序列中的任两个位置,一个加 1 1 1,另一个减 1 1 1,使得这个序列的第 2 − n 2-n 2−n个位置均为 0 0 0。
由于我们要让操作次数尽可能得小,我们要先贪心地去选择在 [ 2 , n ] [2,n] [2,n]区间内的一个正数和一个负数并将它们配对,正数减 1 1 1,负数加 1 1 1。经过了这些操作之后,我们再将剩下的数去与 1 , n + 1 1,n+1 1,n+1中的一个数配对。假设这个数为正,那么配对之后它一定要减 1 1 1;否则一定要加 1 1 1。经历这些操作之后,我们便完成了要求。
假设 2 − n 2-n 2−n这些数中,正数之和为 x x x,负数的绝对值之和为 y y y。首先,我们使用了 m i n ( x , y ) min(x,y) min(x,y)次操作,每次操作将一个正数减 1 1 1,一个负数加 1 1 1。然后,我们使用了 m a x ( x , y ) − m i n ( x , y ) max(x,y)-min(x,y) max(x,y)−min(x,y)次操作,将每一个 [ 2 , n ] [2,n] [2,n]区间内不为 0 0 0的数与下标为 1 1 1或 n + 1 n+1 n+1中的一个数配对。于是我们总共使用了 m a x ( x , y ) max(x,y) max(x,y)次操作。由于第一个位置有 m a x ( x , y ) − m i n ( x , y ) + 1 max(x,y)-min(x,y)+1 max(x,y)−min(x,y)+1种不同的值(即,对于剩下的 m a x ( x , y ) − m i n ( x , y ) max(x,y)-min(x,y) max(x,y)−min(x,y)都可以与下标为 1 1 1的位置配对或不与它配对),所以两个答案分别为:
①
m
a
x
(
x
,
y
)
max(x,y)
max(x,y)
②
m
a
x
(
x
,
y
)
−
m
i
n
(
x
,
y
)
+
1
max(x,y)-min(x,y)+1
max(x,y)−min(x,y)+1
时间复杂度 O ( n ) O(n) O(n)。本题得到完美解决。
Summary
我们通过差分,将区间修改转化为单点修改,自然而然得到了几个性质,从而推出结论。
例2. [LNOI2014]LCA
Description
给定一棵包含 n n n个节点的树, q q q次询问,每次给定 l , r , x l,r,x l,r,x,请你求出 ∑ i = l r d e p t h ( L C A ( i , x ) ) \sum_{i=l}^r depth(LCA(i,x)) i=l∑rdepth(LCA(i,x))
这里的 d e p t h depth depth表示一个节点的深度,即其与根节点的距离加 1 1 1。
Solution
一道绝妙的好题。
首先,我们考虑 d e p t h ( L C A ( i , x ) ) depth(LCA(i,x)) depth(LCA(i,x))等于什么。在这之前,我们先思考 d e p t h depth depth表示什么,以及 L C A LCA LCA表示什么。
①
d
e
p
t
h
depth
depth: 一个节点到根节点的深度加1。
也就是说,从这个节点开始往上走,一直走到根,期间经过的点的数量。
②
L
C
A
LCA
LCA: 两个节点的最近公共祖先。
也就是说,从这两个节点中的一个往上走,对每一个经过的点打上标记;然后另外一个节点也向上走,如果遇到了一个打上标记的节点,立即将这个节点作为它们俩的LCA。
综上所述, d e p t h ( L C A ( i , x ) ) depth(LCA(i,x)) depth(LCA(i,x))就等价于: 让 i i i不停地往上走,对每一个走到的节点打上标记,即将该点的点权加 1 1 1;然后再让 x x x往上走,标记的总数量(即点权之和)就是 d e p t h ( i , x ) depth(i,x) depth(i,x)。
于是, ∑ i = l r d e p t h ( L C A ( i , x ) ) \sum_{i=l}^r depth(LCA(i,x)) ∑i=lrdepth(LCA(i,x))也可以通过类似上述方法来求出。
首先,本题的询问似乎很难处理,而且询问也没有强制在线,于是我们考虑离线。同时,我们再将一个询问拆成两个询问,即查询 [ 1 , r ] [1,r] [1,r]与 [ 1 , l − 1 ] [1,l-1] [1,l−1],二者相减即可。
然后,我们对于现在的 2 m 2m 2m个询问从小到大排序。每次加入一个新节点,就将它到根节点的路径上所有节点的点权都加 1 1 1;对于每一次询问,查询它到根节点路径上每个节点的点权之和。这相当于“路径修改,路径查询”,可以通过重链剖分套线段树在 O ( q log 2 n ) O(q \log^2 n) O(qlog2n)的代价下搞定。
总时间复杂度 O ( n + q log 2 n ) O(n+q \log^2 n) O(n+qlog2n)。注意取模即可。
Summary
有时,区间查询可以转化为更容易处理的前缀查询,方便我们“莫队”离线处理。同时,本题也考察了树链剖分,是一道不可多得的LNOI好题。
例3. [GXOI/GZOI2019]旧词(改编)
Description
给定一棵包含 n n n个节点的树和一个常数 k ( 1 ≤ k ≤ 1 0 9 ) k(1≤k≤10^9) k(1≤k≤109), q q q次询问,每次给定 l , r , x l,r,x l,r,x,请你求出 ∑ i = l r d e p t h ( L C A ( i , x ) ) k \sum_{i=l}^r depth(LCA(i,x))^k i=l∑rdepth(LCA(i,x))k
这里的 d e p t h depth depth表示一个节点的深度,即其与根节点的距离加 1 1 1。
由于答案可能很大,请将其对 998244353 998244353 998244353取模。
Solution
我们仍然将每一个询问拆成两个,离线下来排序。但是 k ≠ 1 k≠1 k=1似乎很难办……而且 k k k这么大,二项式定理拆开都不行……
别慌,我们考虑能不能通过一步差分,将本题转化为例2。定义 A i = ( d e p t h i + 1 ) k − d e p t h i k A_i=(depth_{i}+1)^k-depth_{i}^k Ai=(depthi+1)k−depthik,对于新加入的一个节点,从它到根节点的路径上每个节点都加上一个 A i A_i Ai;对于一次查询,仍然查询它到根节点的路径上所有节点的点权之和。我们可以处理出 A A A的前缀和,然后通过树剖套线段树求出。
这种做法为什么是正确的呢?根据差分的性质,从一个节点到根节点的路径上求出点权之和,就是这个节点本身的贡献之一。于是,每次查询我们都能得到正确的答案。
总时间复杂度仍然为 O ( n + q log 2 n ) O(n+q \log^2 n) O(n+qlog2n)。
一些练习题
①P6070
先挖一个比较显然的性质,然后直接二分差分即可。
②[ABC155F]Perils in Parallel
一道套路题,通过差分可以转化为单点修改,然后建图乱搞即可。
③P3620
一道经典题,通过差分进行等效转化即可。