Integers Have Friends 区间gcd + 双指针

题目link

思路

对于相邻的两个数 a b
区间模 p p p 同余 a % p = k 1 ∗ x + c , b % p = k 2 ∗ x + c a\%p = k1*x+c , b\%p = k2*x+c a%p=k1x+c,b%p=k2x+c 推出 ( a − b ) % p = k ∗ x (a-b) \% p = k*x (ab)%p=kx
考虑差分数组,若在差分数组中一段区间 [ l , r ] , g c d > 1 [l,r] ,gcd >1 [l,r],gcd>1 .则在原数组区间 [ l − 1 , r ] [l-1,r] [l1,r] 模同一个数 x x x 同余
维护区间 g c d gcd gcd 可以 线段树并且可以动态维护
这里不需要修改 S T ST ST 表也可维护区间 g c d gcd gcd
如何枚举答案?
暴力枚举不可取,由于区间 g c d gcd gcd 具有单调性,可以二分 也可以
枚举右端点 r r r,然后移动左端点 l l l

代码

#define int long long
#define endl "\n"
#define _orz ios::sync_with_stdio(false),cin.tie(0)
#define mem(str,num) memset(str,num,sizeof(str))
#define forr(i,a,b) for(int i = a; i <= b;i++)
#define forn(i,n) for(int i = 0; i < n; i++)
#define dbg() cout <<"0k!"<< endl;
typedef long long ll;
int pri[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
const int inf = 0x3f3f3f3f;
const int INF = ~0ULL;
const int N = 1e6+10;
int a[200005],b[200005];

// struct node{
//     int l,r;
//     int dat;
//     #define ls i<<1
//     #define rs i<<1|1
// }t[200005<<2];

// void build(int i,int l,int r){
//     t[i].l = l,t[i].r = r;
//     if(l==r){
//         t[i].dat = b[l];
//         return;
//     }
//     int mid = (l+r) >> 1;
//     build(ls,l,mid);
//     build(rs,mid+1,r);
//     t[i].dat = __gcd(t[ls].dat,t[rs].dat);
// }

// int ask(int i,int l,int r){
//     if(l <= t[i].l && r >= t[i].r){
//         return t[i].dat;
//     }
//     int res  = 0;
//     int mid = (t[i].l+t[i].r)>>1;
//     if(l <= mid) res = __gcd(res,ask(ls,l,r));
//     if(r >mid ) res = __gcd(res,ask(rs,l,r)); 
//     return abs(res);
// }


int f[200005][21];
int n;
void ST(){
    // 不用初始化
    forr(i,1,n) f[i][0] = b[i];
    forr(i,1,20)for(int j = 1;j+(1<<i)-1 <= n;j++){
        f[j][i] = __gcd(f[j][i-1],f[j+(1<<(i-1))][i-1]);
    }
}

void solve(){
    cin>>n;
    forr(i,1,n) cin>> a[i],b[i] = abs(a[i]-a[i-1]);
    ST();
    if(n==1){
        cout << 1 << endl;
        return;
    }
    //build(1,1,n);
    int l = 2; // 细节点 因为差分数组b[1]与原数组相同 所以我们要从l=2开始枚举,因为有时会出现b[1]可能会在提供答案的区间内导致答案+1,例如样例2
    int res = 1;
    for(int r = 2;r <= n;r++){
        if(b[r]==1) continue;
        while(l < r){
            int len =__lg(r-l+1);
            if(__gcd(f[r-(1<<len)+1][len],f[l][len]) == 1) l++;
            else break;
        }
        res = max(res,r-l+2);
    }
    cout << res << endl;
}


signed main()
{
    _orz;
    int t;cin>>t;
    while(t--) solve();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值