CCPC 2018 桂林-Greatest Common Divisor(最大公约数)(差分的应用)

There is an array of length n, containing only positive numbers.
Now you can add all numbers by 1 many times. Please find out the minimum times you need to perform to obtain an array whose greatest common divisor(gcd) is larger than 1 or state that it is impossible.
You should notice that if you want to add one number by 1, you need to add all numbers by 1 at the same time.

有一个长度为n的数组,仅仅包含正整数。现在你可以多次的把所有数都加1。如果可能,请找出使得整个数组所有元素的最大公约数大于1的时候所需要的最少操作次数。

你应当注意:如果你想把一个数加1,你得把所有数都加1

输入

The first line of input file contains an integer T (1≤T≤20), describing the number of test cases.

输入文件第一行包含一个整数T,表示数据组数
Then there are 2×T lines, with every two lines representing a test case.

接下来2T行,每两行都是一组测试数据
The first line of each case contains a single integer n (1≤n≤105) described above.

每组数据第一行都包含一个如上所述的整数n
The second line of that contains n integers ranging in [1,109].

第二行包含n个整数,在1~1e9范围内。

输出

You should output exactly T lines.

你应当输出正正好好的T行
For each test case, print Case d: (d represents the order of the test case) first. Then output exactly one integer representing the answer. If it is impossible, print -1 instead.

对于每组测试样例,输出Case d:然后输出一个整数代表答案,如果无法实现这样的操作,代以输出-1。

输入

3
1
2
5
2 5 9 5 7
5
3 5 7 9 11

输出

Case 1: 0
Case 2: -1
Case 3: 1

提示

Sample 1: You do not need to do anything because its gcd is already larger than 1.
Sample 2: It is impossible to obtain that array.
Sample 3: You just need to add all number by 1 so that gcd of this array is 2.

这个题相当的麻烦,因为每一次变动都涉及全部的数,但是总是有一个量不变:两个数之间的差。差是一个绝对量,由于两个数都是加了1,所以差不会变。差与最大公约数有什么关系呢?

举个例子,2->5->11->14,相邻两个的差值为3,6,3。5比2多了一个3,11比5多了两个3,14比11多了一个3,也就是说在差值不变的情况下,如果可以通过全体加1凑出3的倍数,那么他们的最大公约数顺理成章的就是3,就像3->6->12->15一样。

不过这个想法只在有序的时候使用,而且如果有相同的数,这个想法也会失效,毕竟俩相同的数的差是0,GCD怎可能是0呢?所以我们就得利用std::sort与std::unique这两个函数完成排序与去重。

当然,这个题目还有一个特殊情况:整个数组就只有一个数时,如果它是1,那么就需要用一步把它加到2,反之不需要操作。

所以有如下思路:

  1. 完成排序与去重
  2. 找到每两个数之间差的最大公约数,并作判断,如果得到结果为1,可以直接否定它的可能性
  3. 通过与第一项求最大公约数,来最小化前述所找的结果(第一项实际上是a1-a0,但是a0为0被忽略了),但是不能把它最小化为1(互质则不进行化简操作),否则整个结果就是错误的了。
  4. 为了进一步化简,找到这个最大公约数的最小因子(除了1)。
  5. 第四步获得的数与a1进行取模运算,如果a1%最简gcd恰好为0,这就意味着全体数列是以上非1结果的整数倍,不必进行运算。否则就需要减去这个值(这代表a1需要加多少个1才能到达最简gcd的整数倍)。

 Caution:long long的取模运算慢于int

#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <algorithm>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf("%lld",&a)
#define din(a) scanf("%d",&a)
#define printlnlld(a) printf("%lld\n",a)
#define printlnd(a) printf("%d\n",a)
#define printlld(a) printf("%lld",a)
#define printd(a) printf("%d",a)
#define reset(a,b) memset(a,b,sizeof(a))
const long long int INF=0x3f3f3f3f;
using namespace std;
const double PI=acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod=1000000007;
const int tool_const=1999112620000907;
const int tool_const2=33;
inline int Unnamed_Scanner()
{
    int tmp=0,si=1;
    char c=getchar();
    while(c>'9'||c<'0')
    {
        if(c=='-')
            si=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        tmp=tmp*10+c-'0';
        c=getchar();
    }
    return si*tmp;
}
///Schlacht von Stalingrad
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
int a[5645656];
int gcd(int x,int y)
{
    return y==0?x:gcd(y,x%y);
}
int DETERMINATION()
{
    int t;
    din(t);
    int cnt=1;
    while(t--)
    {
        int n;
        din(n);
        for(int i=1; i<=n; i++)
        {
            din(a[i]);
        }
        sort(a+1,a+1+n);//排序
        int m;
        m=unique(a+1,a+1+n)-(a+1);//去重
        printf("Case %d: ",cnt++);
        if(m==1)//特判
        {
            //cout<<"!"<<endl;
            if(a[1]==1)
                printf("1\n");
            else
                printf("0\n");
            continue;
        }
        int GCD=a[2]-a[1];
        for(int i=3; i<=m; i++)
            GCD=gcd(GCD,a[i]-a[i-1]);
        if(GCD<=1)
        {
            printf("-1\n");
            continue;
        }
        else
        {
            int ans=GCD;
            if(gcd(GCD,a[1])>1)//第一次化简
              GCD=gcd(GCD,a[1]);
            for(int i=2; i<=1006000; i++)//第二次化简
            {
                if(GCD%i==0)
                {
                    ans=i;
                    break;
                }
            }
            if(a[1]%ans==0)//已经达成需求,不必操作
                ans=0;
            else
                ans=(ans-a[1]%ans);//作差。
            printlnd(ans);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43874261/article/details/89949622