题意
给定(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;
}