zoukankan      html  css  js  c++  java
  • Greatest Common Divisor(gcd性质)

    题意

    给定(n)个正整数(a_i),问它们至少同时加多少,可以使得它们的最大公约数大于(1)

    若不存在,则输出(-1)

    数据范围

    (1 leq T leq 20)
    (1 leq n leq 10^5)
    (1 leq a_i leq 10^9)

    思路

    最大公约数性质

    • 更相减损术(引用李煜东《算法竞赛进阶指南》)

    (forall a, b in mathbb{N}, a geq b),有(gcd(a, b) = gcd(b, a - b) = gcd(a, a - b))

    (forall a, b in mathbb{N}),有(gcd(2a, 2b) = 2gcd(a, b))

    证明:

    根据最大公约数的定义,后者显然成立,我们主要证明前者。

    对于(a, b)的任意公约数(d),因为(d|a, d|b),所以(d|(a - b))。因此(d)也是(b, a - b)的公约数,反之也成立。故(a, b)的公约数集合与(b, a - b)的公约数集合相同。于是,它们的最大公约数自然也相等。对于(a, a - b)同理。

    • 推论
      (forall a, b, c in mathbb{N}, a leq b leq c),有(gcd(a, b, c) = gcd(a, b - a, c - b))

    证明:

    (gcd(a, b, c) = gcd((a, b), (b, c)) = gcd((a, b - a), (b, c - b)) = gcd(a, b - a, b, c - b))

    由于(gcd(a, b - a) = gcd(b, b - a)),则(gcd(a, b - a, b, c - b) = gcd(a, b - a, c - b) = gcd(a, b, c))

    本题做法

    我们再回到本题中来,这道题的最终目标就是(gcd(a_1 + k, a_2 - a_1, a_3 - a_2,dots, a_n - a_{n - 1}) > 1)

    我们可以将其拆成两部分,第一部分是(a_1 + k),第二部分是(a_2 - a_1, a_3 - a_2, dots, a_n - a_{n - 1})

    若后一部分的最大公约数是(1),那么两部分的最大公约数就肯定是(1),这种情况就是无解的。

    若后一部分的最大公约数是(x, x e 1),那么我们就考察(x)及其约数与第一部分的关系。我们不妨设(x)及其约数为(q),我们需要找的是满足(a_1 + k = sq, s > 1)的最小值。

    (a_1 = sq - k)可得,(k = lceil a_1 / q ceil * q - a_1)

    最后要注意的一点是所有数相同的情况,若所有数都是(1),那么输出(1);若都不是(1),那么输出(0)

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    typedef long long ll;
    
    const int N = 100010;
    
    int n;
    ll a[N];
    
    ll gcd(ll a, ll b)
    {
        return b ? gcd(b, a % b) : a;
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        for(int cas = 1; cas <= T; cas ++) {
            scanf("%d", &n);
            ll sum = 0;
            for(int i = 1; i <= n; i ++) {
                scanf("%lld", &a[i]);
                sum += a[i];
            }
            if(sum == n) {
                printf("Case %d: 1
    ", cas);
                continue;
            }
            ll tmp = a[1];
            for(int i = 2; i <= n; i ++) tmp = gcd(tmp, a[i]);
            if(tmp > 1) {
                printf("Case %d: 0
    ", cas);
                continue;
            }
            sort(a + 1, a + n + 1);
            tmp = a[2] - a[1];
            for(int i = 2; i < n; i ++) tmp = gcd(tmp, a[i + 1] - a[i]);
            if(tmp == 1) {
                printf("Case %d: -1
    ", cas);
                continue;
            }
            ll ans = ((a[1] - 1) / tmp + 1) * tmp - a[1];
            for(ll i = 2; i <= tmp / i; i ++) {
                if(tmp % i) continue;
                ll k = tmp / i;
                ans = min(ans, ((a[1] - 1) / i + 1) * i - a[1]);
                ans = min(ans, ((a[1] - 1) / k + 1) * k - a[1]);
            }
            printf("Case %d: %d
    ", cas, ans);
        }
        return 0;
    }
    
  • 相关阅读:
    Inception V1-V4
    NDCG的理解
    进程与线程
    Java中的接口和抽象类
    HashMap的工作原理
    基于比较排序的算法复杂度的下界
    数据库-left join,right join,inner join,full join
    外排序 External sorting
    数据流中的中位数 Find Median from Data Stream
    Codeforces Round #272 (Div. 2)
  • 原文地址:https://www.cnblogs.com/miraclepbc/p/14380751.html
Copyright © 2011-2022 走看看