從這看的
對於一些求滿足某一性質的最長區間的問題,可以考慮 O(n) 的雙指針(或者多帶個 log:ST表預處理然后枚舉右端點對左端點二分,或者線段樹)
常規的雙指針要求向當前維護的集合中加入一個元素、刪除一個元素時,都能較快地更新集合的某種函數值或性質(當然,根本上要求集合隨着元素的加入,這種“函數值或性質”滿足單調性,如減小、聯通性)
這個算法可以做到無需刪除,前提是維護的得是函數值(或者要求能夠以某種形式存下述的東西?)
設左、右、中間端點 l, r, mid,當前滿足條件的區間為 [l, r],數組 resl[l] 存儲 [l, mid] 的函數值,resr 存儲 (mid, r] 的函數值
當前 r++,接着若 [l, r] 不滿足“某條件”則 l++,同時直接調用 resl[l],其與 resr 之並即為 [l, r] 的更新值
直到 [l, r] 滿足“某條件”;或者 l > mid,此時令 mid = r, l = r+1,然后不斷令 l-- 並更新 resl,直到 [l, mid] 不再滿足“某條件”(有可能一開始就不滿足條件,此時區間為 [r+1, r],區間長即為 0)
l 和 r 都只會各自遍歷數組一遍,總的復雜度是 O(n*加入一個元素的復雜度)
求區間最長的 gcd>1 的段(常規做法就是ST表或線段樹,另外注意轉化!涉及同余可以考慮移到等號一邊,考慮差分數組!)
題目
#include <cstdio>
using namespace std;
typedef long long ll;
const int MAXN = 200005;
int T, N;
ll A[MAXN], resl[MAXN], resr;
ll ab(ll x) { return x < 0 ? -x : x; }
ll gcd(ll x, ll y) { return y ? gcd(y, x%y) : x; }
int max(int x, int y) { return x > y ? x : y; }
int main()
{
for (scanf("%d", &T); T; T--) {
scanf("%d", &N);
for (int i=1; i<=N; i++) scanf("%lld", &A[i]);
if (N==1) { puts("1"); continue; }
for (int i=2; i<=N; i++) A[i-1] -= A[i], A[i-1] = ab(A[i-1]);
//for (int i=1; i< N; i++) printf("%lld ", A[i]); puts("");
int l = 1, r = 1, mid = 1, ans = A[1] > 1; // [l, mid], (mid, r];
resl[1] = A[1]; if (A[1]==1) l = mid+1;
while (r< N-1) {
++r, resr = r==mid+1 ? A[r] : gcd(resr, A[r]);
while (l<=mid && gcd(resl[l], resr)==1) l++;
if (l> mid) {
mid = r, l = r+1, resl[l] = A[l-1];
while (l> 1 && (resl[l-1]=gcd(resl[l], A[l-1]))> 1) l--;
}
ans = max(ans, r-l+1);
//printf("[%d, %d]\n", l, r);
}
printf("%d\n", ans+1);
}
}