ccpc2018桂林 G. Greatest Common Divisor

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,ab)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(ba,cb)
设操作后,序列的最大公约数为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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值