G. Greatest Common Divisor
题目大意
给出n个数,每一次操作可以使所有数+1,问如果要使这些数的gcd>1,需要最少的操作次数。
题解
我们知道a>b的时候有
gcd
(
a
,
b
)
=
gcd
(
a
,
a
−
b
)
(
公
式
1
)
\gcd(a,b)=\gcd(a, a-b)(公式1)
gcd(a,b)=gcd(a,a−b)(公式1)
所以c>b>a的时候有
gcd
(
a
+
k
,
b
+
k
,
c
+
k
)
=
gcd
(
b
−
a
,
c
−
b
)
\gcd(a+k,b+k,c+k)=\gcd(b-a,c-b)
gcd(a+k,b+k,c+k)=gcd(b−a,c−b)
设操作后,序列的最大公约数为t。因此我们可以求出这n个数两两之差,这n*n个数的gcd,一定是t的倍数。
这n*n个数的最大公约数,根据公式1,等价于将这n个数排序后,相邻两个数的n-1个差的最大公约数。
那我们题目就可以改为:给出n个数,如何操作可以使这n个数的最大公约数为t;其中t是gcd的约数,使得操作次数最少。
这里的约数一定是质因数。因为合因数的倍数一定是质因数的倍数,反之不然。
当gcd=1,输出-1。
我们可以求出gcd,然后对于gcd的每一个质因数t,都制造出最小的p,使得p * t >= a[1]。并找出max{p * t - a[1]}
可以轻易证明,当a[1]加上某一最小的数是质因数t的倍数时,其他数也一定是质因数t的倍数。
最后还需要特判,所有数都相等的时候、只有一个数的时候,两种特殊情况。
#include<bits/stdc++.h>
using namespace std;
int read () {
int num = 0; char c = ' '; int flag = 1;
for (;c > '9' || c < '0'; c = getchar ())
if (c == '-')
flag = 0;
for (;c >= '0' && c <= '9'; num = (num << 3) + (num << 1) + c - 48, c = getchar ());
return flag ? num : - num;
}
const int maxn = 100200;
int a[maxn], n;
int gcd (int a, int b) {
if (b == 0) return a;
return gcd (b, a % b);
}
void init () {
n = read ();
for (int i = 1;i <= n;i ++)
a[i] = read ();
sort (a + 1, a + 1 + n);
}
int tar;
void getgcd () {
tar = a[2] - a[1];
for (int i = 2;i <= n;i ++)
tar = gcd (tar, a[i] - a[i - 1]);
}
long long work (int tar) {
long long l = 1, r = a[1];
while (l < r) {
long long mid = (l + r) >> 1;
if (mid * tar >= a[1]) r = mid;
else l = mid + 1;
}
long long ans = r * tar - a[1];
return ans;
}
int prime[1100000], primesize = 0, phi[11000000]; bool isprime[11000000];
void getlist (int listsize) {
memset (isprime, 1, sizeof isprime);
isprime[1] = false;
for (int i = 2;i <= listsize;i ++) {
if (isprime[i]) prime[++ primesize] = i;
for (int j = 1;j <= primesize && i * prime[j] <= listsize;j ++) {
isprime[i * prime[j]] = false;
if (i % prime[j] == 0) break;
}
}
}
int divisor[1000000], top;
void dividegcd () {
top = 0;
for (int i = 1;prime[i] * prime[i] <= tar;i ++)
if (tar % prime[i] == 0) {
divisor[++ top] = prime[i];
while (tar % prime[i] == 0)
tar /= prime[i];
}
if (tar > 1) divisor[++ top] = tar;
}
int main () {
int T = read ();
getlist (100000);
for (int k = 1;k <= T;k ++) {
init ();
if (n == 1) {
if (a[1] == 1) printf ("Case %d: %d\n", k, 1);
else printf ("Case %d: %d\n", k, 0);
continue;
}
if (a[1] == a[n] && a[1] == 1) {
printf ("Case %d: %d\n", k, 1);
continue;
}
if (a[1] == a[n]) {
printf ("Case %d: %d\n", k, 0);
continue;
}
getgcd ();
if (tar == 1) {
printf ("Case %d: %d\n", k, -1);
continue;
}
dividegcd ();
long long ans;
ans = 2e9; ans *= ans;
for (int i = 1;i <= top;i ++)
ans = min (ans, work (divisor[i]));
printf ("Case %d: %lld\n", k, ans);
}
return 0;
}