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 \ne 1\),那么我们就考察\(x\)及其约数与第一部分的关系。我们不妨设\(x\)及其约数为\(q\),我们需要找的是满足\(a_1 + k = sq, s > 1\)的最小值。

\(a_1 = sq - k\)可得,\(k = \lceil a_1 / q \rceil * 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\n", 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\n", 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\n", 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\n", cas, ans);
    }
    return 0;
}
posted @ 2021-02-06 10:20  pbc的成长之路  阅读(305)  评论(3编辑  收藏  举报