Codeforces Round #736 (Div. 2) D. Integers Have Friends(数论、ST表、二分、双指针)

  • 题目:Integers Have Friends

  • 题意:给出一个序列ai,规定一种友好组合为组合中所有数对m(m >= 2)取余的结果相同,问从该序列中寻找这样友好组合的子串,长度最大能为多少。

  • 思路:ST表预处理区间gcd,通过二分枚举长度/双指针的方法找最大长度符合要求的子串。

  • 解析:根据题意,首先要了解同余定理:

    • \[\begin{align*} &a_i\equiv b_{i}(mod\ m)\\ =>\ & abs(b_{i} - a_i)\ mod\ m = 0 \end{align*} \]

    • 首先令: d[i] = abs(a[i+1] - a[i]),若一个序列所有数对m求余后得到的数相同,其子序列的差分数组一定可以被m整除,说明其子序列的所有差分数组最大公约数一定是相同并且大于1,那么我们可以利用ST表/线段树来计算出该序列所有区间的gcd,再利用二分枚举区间长度判断是否存在一个子序列满足其所有差分数组的值的gcd不为1,或者利用双指针判断一样可以。

  • 代码(二分):

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int N = 2e5 + 5;
    typedef long long ll;
    ll a[N], d[N], f[N][22]; // f(i, j)表示区间[i, i + 2^j - 1]所有数的gcd
    int t, n;
    
    ll gcd(ll x, ll y)
    {
        return !y ? x : gcd(y, x % y);
    }
    
    void st_init()
    {
        for(int i = 1; i < n; i++) f[i][0] = d[i];
        for(int j = 1; j <= log2(n); j++)
            for(int i = 1; i < n - (1 << j) + 1; i++)
                f[i][j] = gcd(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
    }
    
    ll query(int l, int r)
    {
        int len = r - l  + 1;
        int k = log2(len);
        return gcd(f[l][k], f[r - (1 << k) + 1][k]);
    }
    
    int check(int len)
    {
        for(int i = 1; i + len - 1 < n; i++)
        {
            if(query(i, i + len - 1) > 1) return 1;
        }
        return 0;
    }
    
    int main()
    {
        scanf("%d", &t);
        while(t --)
        {
            scanf("%d", &n);
            for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
            for(int i = 1; i < n; i++) d[i] = abs(a[i + 1] - a[i]); //预处理差分数组
            st_init();
            if(n == 1) //特判
            {
                printf("1\n");
                continue;
            }
            int l = 0, r = n - 1; //二分枚举长度
            while(l < r)
            {
                int mid = (l + r + 1) / 2;
                if(check(mid)) l = mid;
                else r = mid - 1;
            }
            printf("%d\n", l + 1);
        }
        return 0;
    }
    
    
  • 代码(双指针):

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int N = 2e5 + 5;
    typedef long long ll;
    ll a[N], d[N], f[N][22];
    int t, n;
    
    ll gcd(ll x, ll y)
    {
        return !y ? x : gcd(y, x % y);
    }
    
    void st_init()
    {
        for(int i = 1; i < n; i++) f[i][0] = d[i];
        for(int j = 1; j <= log2(n); j++)
            for(int i = 1; i < n - (1 << j) + 1; i++)
                f[i][j] = gcd(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
    }
    
    ll query(int l, int r)
    {
        int len = r - l  + 1;
        int k = log2(len);
        return gcd(f[l][k], f[r - (1 << k) + 1][k]);
    }
    
    int main()
    {
        scanf("%d", &t);
        while(t --)
        {
            scanf("%d", &n);
            for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
            for(int i = 1; i < n; i++) d[i] = abs(a[i + 1] - a[i]);
            st_init();
            if(n == 1) //特判
            {
                printf("1\n");
                continue;
            }
            int res = 0;
            for(int i = 1, j = 1; i < n && j < n; i++)
            {
                while(j <= i && query(j, i) == 1) j++;
                res = max(res, i - j + 2);
            }
            printf("%d\n", res);
        }
        return 0;
    }
    
    
posted @ 2021-08-05 17:10  ~K2MnO4  阅读(157)  评论(0)    收藏  举报