CF1593D1题解
CF1593D1题解
题意
把题意转换一下就可以得到:给定一个数列 \(a_1,a_2,a_3,\cdots,a_n\),要求一个数列 \(b\)(\(b\) 中的数为非负整数)和一个正整数 \(k\),使得 \(a[1]-b[1]\times k=a[2]-b[2]\times k=\cdots=a[n]-b[n]\times k\)。若有无数多个 \(k\),请输出 \(-1\)。(有多组数据)
思路
首先直接讲解法:将数组去重并判断是否有无数多个 \(k\) 之后再两两作差求最大公因数即可。那么就有以下解释:
- 咋去重?答:建立一个 \(vis\) 数组和 \(a\) 数组,\(vis\) 数组为判断一个数是否已经出现过,\(a\) 数组为去重之后的数组,如果一个数没有出现过,就录入到数组 \(a\) 中。
- 咋判断有无数多个 \(k\)?答:在去重的时候,用一个 \(len\) 记录去重之后的数组长度,如果为 \(1\),也就是去重以前所有数都相同,所以 \(k\) 为任意数,输出 \(-1\)。
- 为啥用两两相减求最大公因数?接下来我就来证明。
证明:
\(\because a_i-b_i\times k=a_j-b_j\times k\)
\(\therefore a_i-a_j=k\times(b_i-b_j)\)
\(\therefore a_i\equiv a_j\pmod k\)
\(\therefore a_1\equiv a_2\equiv a_3\equiv\cdots\equiv a_n\pmod k\)
\(\therefore\) 只要两两作差求最大公因数即可。
总结
- 多组数据。
- 换行。
- 两两作差求最大公因数。
- 去重。
- 特判。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int T;
int n,a[45],ans,cnt,len;
bool vis[2000005];
//a数组为去重之后的数组
//cnt为临时变量。
//len为去重之后的数组长度。
//vis数组是一个数字是否出现过的统计数组,去重要用。
int gcd(int a, int b){//求最大公因数
if(b==0) return a;
return gcd(b,a%b);
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
memset(vis,0,sizeof(vis));//一定要重置。
len=0;
for(int i=1; i<=n; i++){
scanf("%d",&cnt);
if(vis[cnt+1000001]==0){//以前这个数没出现过(这个数是新的)。
vis[cnt+1000001]=1;//标记为出现过(旧的)。
a[++len]=cnt;//录入a数组
}
}
if(len==1){//去重之后只有1个数,也就是去重以前所有数都相同,所以k为任意数,输出-1。
printf("-1\n");//别忘了换行
continue;
}
ans=abs(a[2]-a[1]);//取绝对值
for(int i=2; i<len; i++)
for(int j=i+1; j<=len; j++)
ans=gcd(ans,abs(a[j]-a[i]));
printf("%d\n",ans);
}
return 0;
}
附(选看)
怎么求最大公因数
方法 \(1\):用唯一分解定理,先分解质因数,然后求最大公因数。
令 \(a=p_1^{a_1}\times p_2^{a_2}\times p_3^{a_3}\times \cdots p_m^{a_m}\),\(b=p_1^{b_1}\times p_2^{b_2}\times p_3^{b_3}\times \cdots p_m^{b_m}\)。
则 \(\gcd(a,b)=p_1^{\min(a_1,b_1)}\times p_2^{\min(a_2,b_2)}\times p_2^{\min(a_2,b_2)}\times\cdots p_m^{\min(a_m,b_m)}\)。
方法 \(2\):九章算术 \(\bullet\) 更相减损术。
\(\forall a,b\in \mathbb{N}\),\(b\le a\),有 \(\gcd(a,b)=\gcd(b,a-b)=\gcd(a,a-b)\)。
\(\forall a,b\in \mathbb{N}\),有 \(\gcd(2a,2b)=2\gcd(a,b)\)。
证明:
对于 \(a\),\(b\) 的任意公约数 \(d\),因为 \(d\vert a\),\(d\vert b\),所以 \(d\vert (a-b)\)。因此,\(d\) 为 \(b\),\((a-b)\) 的公约数。反之亦成立。
方法 \(3\):欧几里得算法。
\(\forall a,b\in \mathbb{N}\),\(b\neq0\),\(\gcd(a,b)=\gcd(b,a\bmod b)\)。
证明:
若 \(a<b\),则 \(\gcd(b,a\bmod b)=\gcd(a,b)\),命题成立。
若 \(a\geq b\),设 \(a=q\times b+r\),其中 \(0\le r<b\)。显然 \(r=a\bmod b\)。对于 \(a\),\(b\) 的任意公约数 \(d\),因为 \(d\vert a\),\(d\vert q\times b\),所以 \(d\vert (a-qb)\)。即 \(d\vert r\),因此,\(d\) 为 \(b\),\(r\) 的公约数。反之亦成立。所以他们的最大公因数相等。
代码实现:
int gcd(int a, int b){
if(b==0) return a;
return gcd(b,a%b);
}