传送门
由于在此题解中加入了许多自己对细节的处理,所以长文警告(雾)
Description
给定一个长度为 \(N\) 的数列 \(A\),以及 \(M\) 条指令,每条指令可能是以下两种之一:
C l r d,表示把 A[l],A[l+1],…,A[r] 都加上 \(d\)。
Q l r,表示询问 A[l],A[l+1],…,A[r] 的最大公约数 (GCD)。
对于每个询问,输出一个整数表示答案。
Constraints
\(N≤500000,M≤100000,1≤A[i]≤10^{18},|d|≤10^{18}\)
Solution
Part 1 主要思路
我们首先的思路是考虑能不能直接去维护两种操作,很可惜这题不行。
由于这题在求 gcd 同时还会区间加,而可能 \(gcd(a, b)≠gcd(a+x, b+x)\),不满足子区间可合并性,所以不能直接去维护。
除了辗转相除法求 gcd 以外,我们还能再想到效率稍低的更相减损术(证就不证了):
\(gcd(a, b)=gcd(a, b-a)\)
此式子可以推广到多个数,由此可推广到本题,设查询这个区间的左右端点分别为 \(l\)、\(r\),则有:
\(\qquad \qquad \qquad \qquad gcd(a[l], a[l+1], a[l+2],..., a[r]) = gcd(a[l], a[l+1] - a[l], a[l + 2] - a[l+1],...,a[r]-a[r-1])\)
因此,我们所查询的左式可以转化为右式。
观察右式,可以发现除第一项 \(a[l]\) 之外,后面的就是原数组 \(a\) 的差分数组!
设 \(a\) 的差分数组为 \(b\),则
\(\qquad \qquad \qquad \qquad gcd(a[l], a[l+1] - a[l], a[l + 2] - a[l+1],...,a[r]-a[r-1])=gcd(a[l],b[l+1],b[l+2],...,b[r])\)
而去维护差分数组需要维护单点修改与区间 gcd,可以使用线段树或树状数组维护。
同时,每次查询的第一项 \(a[l]\) 是原数组,需要再建立一棵线段树,去维护原数组的区间修改与单点查询。
总结:维护两棵线段树,一棵是原数列,区间修改+单点查询(这么做会TLE,仍需经过处理,在Part2中会讲到),一棵是原序列的差分序列,单点修改+区间查询。
Part 2 一些代码实现的细节
这题我调了6h+,WA了十几发才过,发现了里面一些需要注意的东西(虽然可能大家都知道)
如果您是口胡人当我没说
+ 差分数组中 gcd 会出现负数与 \(0\) 情况
由于 gcd 不能出现负数,又根据 \(gcd(a,b)=gcd(a,-b)\) 可知,直接在手写 gcd 函数注意取绝对值即可。
但是不能直接为差分数组取绝对值!差分数组能反映原数组相邻数之间的增减情况的,不能更改!
同时,差分数组也会有 \(0\),而在 gcd 中 \(mod\) 了 \(0\) 就爆炸了,根据 \(gcd(a,0)=a\) ,对 \(0\) 情况特殊处理一下。
+ 差分数组越界情况
由于差分数组处理是下方这样:
a[l] += x; a[r + 1] -= x;
所以可能导致\(r+1\)下表超出线段树 \(n\) 的范围导致蜜汁 \(MLE\),而 \(r+1\) 位置数值的变化我们并不关心,若 \(r+1 \ge n\) 直接特判不进行操作即可。
在最后输出答案时也要小心,因为也有可能越界!
+ 维护原数组中 \(pushdown\) 下传操作爆 \(long\) \(long\) 的问题
区间最大长度高达 \(10^6\),在下传中乘一下可能 \(10^{18}\) 大小的 \(d\),开 \(long\) \(long\) 也救不回来 


高精度的话复杂度不够,只能想办法避免 \(pushdown\) 操作。
在学习线段数组时,也使用了差分来将区间修改变成了单点修改,在此题中也可以应用。
将维护原数组的线段树也改变成维护差分数组,只不过这次维护的是区间和。
式子又可以变成
\(\qquad \qquad \qquad \qquad gcd(a[l],b[l+1],b[l+2],...,b[r]) = gcd((b[1]+b[2]+...+b[l]),b[l+1],b[l+2],...,b[r])\)
这次都可以正确地维护了。
Code
这是原来的两棵线段树修改而成的,为直观理解,未对两棵线段树的相同操作与函数进行合并。(所以会有点长)
读者在自行写代码时可以把一些函数进行合并,节省码量。
// by youyou2007 in 2022.
#include
#include
#include
#include
#include
#include
#include
#include
#include