Solution - CF1549D

Solution

首先,注意到对于两个数ai,aja_i, a_jmm的最大值为aiaj\left|a_i - a_j\right|,且可以取到。

对于ai,ai+1,,aja_i, a_{i + 1}, \cdots, a_jmm的最大值为gcd(aiai+1,,aj1aj)\gcd(\left|a_i - a_{i + 1}\right|, \cdots, \left|a_{j - 1} - a_j\right|),且可以取到,因为:

  1. mm如果比gcd(aiai+1,,aj1aj)\gcd(\left|a_i - a_{i + 1}\right|, \cdots, \left|a_{j - 1} - a_j\right|)大,那么一定存在akak+1≢0modm    akak+1km(kZ)\left|a_k - a_{k + 1}\right| \not\equiv 0\mod m\iff a_k - a_{k + 1}\neq km(k\in\textbf{Z}),矛盾。
  2. m=gcd(aiai+1,,aj1aj)m=\gcd(\left|a_i - a_{i + 1}\right|, \cdots, \left|a_{j - 1} - a_j\right|)时,任意两个数akah=(akak+1)+(ak+1ak+2)++(ah1ah)=(k1+k2+khk)ma_k - a_h = (a_k - a_{k + 1}) + (a_{k + 1} - a_{k + 2}) + \cdots + (a_{h - 1} - a_h) = (k_1 + k_2 + \cdots k_{h - k})m,所以满足条件。

根据以上分析,确定区间就可以直接求出mm。这个时候有两种解决方法:

  1. 利用倍增,每个数往后倍增直到m=1m=1,时间复杂度O(nlognlogmaxai)O(n\log n\log \max a_i)。利用st表可以做到O(nlogmaxai+nlogn)O(n\log\max a_i + n\log n)
  2. 利用baka's trick进行双指针:每一时刻都有三个指针:l,r,midl, r, mid,其中mid[l,r]mid\in [l, r],并且每一时刻[l,mid],[l+1,mid],,[mid1,mid][l, mid], [l + 1, mid], \cdots, [mid - 1, mid]的贡献已经处理好。当ll要跨越midmid的时候,将midmid设为rr,并让llrr倒序遍历回来,处理好新的[l,mid],[l+1,mid],,[mid1,mid][l, mid], [l + 1, mid], \cdots, [mid - 1, mid]的贡献。通过这样反复横跳,可以实现无删的双指针(只要单调且可加就可以双指针)。由于rr总共动nn次,midmid是单调增加的,也最多总共动nn格,而ll除了本身要动的nn下,最多再把midmid走的路径走一遍,也是只要动O(n)O(n)下。故要计算的次数还是O(n)O(n)的。这样时间复杂度就是O(nlogmaxai)O(n\log\max a_i)

Code

Show Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 2e5 + 1;
LL A[N], B[N], rev[N]; int n;
inline LL gcd(LL a, LL b){
while (b){
a %= b;
a ^= b ^= a ^= b;
}
return a;
}
signed main(){
int t; cin >> t;
while (t--){
cin >> n;
for (int i = 1; i <= n; ++i) cin >> A[i];
for (int i = 1; i < n; ++i) B[i] = abs(A[i] - A[i + 1]), rev[i] = 0;
int l = 1, mid = 0;
int ans = 1;
LL sum = 0;
for (int r = 1; r < n; ++r){
sum = gcd(sum, B[r]);
while (l <= mid && gcd(sum, rev[l]) == 1) ++l;
if (l > mid){ // 超过mid时重新计算
mid = r, rev[mid] = B[mid];
for (int i = mid - 1; i >= l; --i){
rev[i] = gcd(rev[i + 1], B[i]);
}
sum = 0;
}
while (l <= mid && gcd(sum, rev[l]) == 1) ++l;
ans = max(ans, r - l + 2);
}
cout << ans << endl;
}
return 0;
}