开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情
[Codeforces] Codeforces Round #736 (Div. 2) D. Integers Have Friends | ST表、二分
题目链接
题目
题目大意
给长为n的数组a,n个数两两不同,要找最长的区间[l, r]使得存在m >1, a[i] % m (l <= i <= r)全相等
数据范围:
测试样例组数t (1 <= t <= 2e4)
每组输入: n (1 <= n <= 2e5), 1 <= a[i] <= 1e18
限制:所有样例的n之和 <= 2e5
思路
对于a[i] % m = a[j] % m, 我们等价为 m | (a[i] - a[j]), 也就是,m必须是a[i] - a[j]的因子
对于一段区间[l, r] , a[i] % m (l <= i <= r)全相等
等价于,对于[l, r) 满足 m | a[i+1]-a[i]
等价于,m | gcd(a[l+1]-a[l], ... , a[r] - a[r-1])
题目还要求 m > 1
所以我们考虑对a数组做差分, 然后要能对差分数组b快速求区间gcd
显然,st表可以做到这一点,O(nlogn)的初始化和O(1)的查询
然后,我们考虑对枚举差分数组b起点i (1 <= i < n), 二分出最大的右端点j, 满足 gcd(a[i], ..., a[j]) > 1
对于每个起点得到的区间长度+1取max就是最终的答案。
需要注意
- n = 1的情况建议特判
- 如果b[i] = 1时 j = i-1的情况,所以二分的起点l初始要小于i,直接设l = i-1即可
代码
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
using namespace std;
const int maxn = 2e6 + 20;
const int mod = 1e9 + 7;
const int inf = 1LL << 60;
#define db 0
#define test if (db)
int n, m, k, t;
void solve() {
cin >> n;
vector<int> v(n);
for (int i = 0; i < n; ++i) cin >> v[i];
if (n == 1) {
cout << "1\n";
return;
}
--n;
vector<int> a(n);
for (int i = 0; i < n; ++i) a[i] = abs(v[i+1] - v[i]);
vector<array<int, 20>> st(n+n);
for (int i = 0; i < 20; ++i) for (int j = 0; j + (1LL << i) - 1 < n; ++j) {
if (!i) st[j][i] = a[j]; else st[j][i] = __gcd(st[j][i-1], st[j+(1<<(i-1))][i-1]);
}
function ask = [&](int l, int r) -> int {
if (l > r) return 1;
int k = __lg(r-l+1);
return __gcd(st[l][k], st[r-(1<<k)+1][k]);
};
int ans = 1;
for (int i = 0; i < n; ++i) {
int l = i-1, r = n-1;
while (l < r) {
int m = (l+r+1) >> 1;
if (ask(i, m) > 1) l = m; else r = m-1;
}
ans = max(ans, r-i+2);
}
cout << ans << '\n';
}
void refresh() {
}
signed main() {
IOS
t = 1;
cin >> t;
while (t--) {
solve();
refresh();
}
return 0;
}