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;
}
原文地址:https://www.cnblogs.com/miraclepbc/p/14380751.html