解题思路:
遇事不决想差分。
初步转换原问题,可以得到:对于这其中所有的 a i a_i ai 满足 a i − k m o d m = 0 a_i-k\bmod m=0 ai−kmodm=0,其中 m ≥ 2 m\ge 2 m≥2。
这里的处理难点就是 k k k 不方便找出,考虑整个数组作差,这样无论 k k k 取什么值,都有原命题的一个充分必要条件: a i ≡ a i + 1 ( m o d m ) a_i\equiv a_{i+1}\pmod m ai≡ai+1(modm),问题转化为差分数组(不妨记作 d d d)中最长的一段 gcd i = l r ( d i ) ≥ 2 \gcd_{i=l}^{r}(d_i) \ge 2 gcdi=lr(di)≥2。
这个问题比较经典了,直接 ST 表预处理出区间 gcd \gcd gcd,然后双指针判断即可,复杂度 O ( n log n ) O(n\log n) O(nlogn)。
注意差分后的数组的位置和原数组不太一样。
代码:
#include<cstdio>
#include<cmath>
using namespace std;
#define int long long
int T,n,l,r,a[200005],f[25][200005],ans;
int gcd(int a,int b){
if(b==0)return a;
return gcd(b,a%b);
}
int query(int l,int r){
int k=log2(r-l+1);
return gcd(f[k][l],f[k][r-(1<<k)+1]);
}
int max(int a,int b){
if(a<b)return b;
return a;
}
signed main(){
scanf("%I64d",&T);
while(T--){
scanf("%I64d",&n);
for(int i=1;i<=n;i++){
scanf("%I64d",&a[i]);
f[0][i]=abs(a[i]-a[i-1]);
}
if(n==1){printf("1\n");continue;}
for(int i=1;i<=20;i++)
for(int j=1;j<=n-(1<<i)+1;j++)
f[i][j]=gcd(f[i-1][j],f[i-1][j+(1<<(i-1))]);
l=1;r=2;ans=0;
while(r<=n){
while(l<r&&query(l+1,r)<2)l++;
ans=max(ans,r-l+1);
r++;
}
printf("%I64d\n",ans);
}
return 0;
}