题意:
给定n个数字,定义一个序列合法: 存在 m>=2 ,使得对于序列中任意数字 ai 有 ai % m 的结果相等。求最长合法序列长度。
问题转换:
对于 ai % m == p 可以转化为 ai = b * m + p, b为任意自然数, 对于 p 的查找, 在1e18的范围内我不会更优的做法, 所以采取消掉p, 及对于相邻 ai ai+1, 有 abs(ai - ai + 1)= b * p。
此时问题转化为, 对于这n个数字的差分数组, 找到一个最长区间, 使得其gcd >= 2(所给限制),区间查找我们可以想到的是线段树或者st表,两者都可。
对于暴力查找所有区间,显然会t掉, 但对于本题, 如果给定了一个右端点, 那么与之匹配的最左左端点的左侧,一定都是非法左端点, 即 对于右端点向右滑行的过程中, 左端点的移动方向是固定的,此时我们可以使用双指针优化查找。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 5e5 + 5;
int a[maxn], e[maxn << 2];
void henshin(int rt){
e[rt] = __gcd(e[rt << 1], e[rt << 1 | 1]);
}
void build(int l, int r, int rt){
if(l == r){
e[rt] = a[l];
return;
}
int m = l + r >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
henshin(rt);
}
int ask(int l, int r, int al, int ar, int rt){
if(al <= l && r <= ar) return e[rt];
int m = l + r >> 1;
int ans = 0;
if(al <= m)
ans = ask(l, m, al, ar, rt << 1);
if(ar > m)
ans = __gcd(ans, ask(m + 1, r, al, ar, rt << 1 | 1));
return ans;
}
signed main(){
int t; scanf("%lld",&t);
while(t--){
int n; scanf("%lld",&n);
for(int i = 1; i <= n; ++i) scanf("%lld",&a[i]);
if(n == 1){
cout << 1 << endl;
continue;
}
n--;
for(int i = 1; i <= n; ++i) a[i] = abs(a[i + 1] - a[i]);
build(1, n, 1);
int ans = 1;
int l = 1;
for(int r = 1; r <= n; ++r){
if(a[r] == 1) continue;
while(l < r){
if(ask(1, n, l, r, 1) == 1) l++;
else break;
}
ans = max(ans, r - l + 2);
}
cout << ans << endl;
}
}