题目描述
难度分:1700
输入T(≤104)表示T组数据。所有数据的n之和≤2×105,q之和≤2×105。
每组数据输入n(1≤n≤2×105),q(1≤q≤2×105)和长为n的数组a(1≤a[i]≤109)。数组下标从1开始。
然后输入q个询问,每个询问输入两个数L、R(1≤L≤R≤n)。
对于每个询问,输出最大的m,满足a[L],a[L+1],…,a[R]关于模m同余(这些数模m的结果都相等)。如果m可以无限大,输出0。
输入样例
3
5 5
5 14 2 6 3
4 5
1 4
2 4
3 5
1 1
1 1
7
1 1
3 2
1 7 8
2 3
1 2
输出样例
3 1 4 1 0
0
1 6
算法
差分数组+ST表
对于任意两个数a[i]和a[j],如果要满足a[i]%m=a[j]%m,那么任意数对(i,j)都有(a[i]−a[j])%m值相等成立。
而对于查询区间[L,R],要想这个区间里面的任意数对(i,j)的(a[i]−a[j])%m相等,其实并不需要枚举数对(i,j),只要相邻元素a[L+1]−a[L],a[L+2]−a[L+1],…,a[R]−a[R−1]模m的值相等就可以了。假设i<j<k,有(a[j]−a[i])%m=(a[k]−a[j])%m成立,(a[i]−a[k])%m=(a[j]−a[i])%m=(a[k]−a[j])%m仍然会满足。
这样可以先预处理出一个diff数组,diff[i]=a[i]−a[i−1]。对于任意询问[L,R]:
- 如果L=R,那m可以无穷大,答案是0。
- 如果L<R,那m就是a[L+1]−a[L],a[L+2]−a[L+1],…,a[R]−a[R−1]的最大公约数,求一个diff在[L+1,R]上的最大公约数即可。
考虑到本题并不会修改a数组,求区间最大公约数可以预处理出一个gcd的ST表,这样就能O(1)求得区间最大公约数了。
复杂度分析
时间复杂度
将求最大公约数的时间复杂度近似看成常数,预处理出ST表的时间复杂度为O(nlog2n)。有了ST表,后续的每个询问就可以O(1)计算出来,因此后面询问的时间复杂度为O(q)。因此,整个算法的时间复杂度为O(nlog2n+q)。
空间复杂度
空间消耗包括两个部分,一个是a的差分数组diff,它和a同规模,消耗空间为O(n)。另一个是ST表,空间消耗为O(nlog2n)。因此,空间瓶颈在于ST表,整个算法的额外空间复杂度为O(nlog2n)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010, M = 19;
int T, n, q, a[N], diff[N], st[N][M];
int query(int l, int r) {
int k = log2(r - l + 1);
return __gcd(st[l][k], st[r - (1<<k) + 1][k]);
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if(i >= 2) diff[i] = abs(a[i] - a[i - 1]);
}
for(int j = 0; j < M; j++) {
for(int i = 1; i + (1 << j) - 1 <= n; i++) {
if(!j) {
st[i][j] = diff[i];
}else {
st[i][j] = __gcd(st[i][j - 1], st[i + (1<<(j - 1))][j - 1]);
}
}
}
while(q--) {
int l, r;
scanf("%d%d", &l, &r);
printf("%d ", l < r? query(l + 1, r): 0);
}
puts("");
}
return 0;
}