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 之后再两两作差求最大公因数即可。那么就有以下解释:

  1. 咋去重?答:建立一个 vis 数组和 a 数组,vis 数组为判断一个数是否已经出现过,a 数组为去重之后的数组,如果一个数没有出现过,就录入到数组 a 中。
  2. 咋判断有无数多个 k?答:在去重的时候,用一个 len 记录去重之后的数组长度,如果为 1,也就是去重以前所有数都相同,所以 k 为任意数,输出 -1。
  3. 为啥用两两相减求最大公因数?接下来我就来证明。

证明:

\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 ### 总结 1. 多组数据。 2. 换行。 3. 两两作差求最大公因数。 4. 去重。 5. 特判。 ### 代码 ```cpp #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$ 的公约数。反之亦成立。所以他们的最大公因数相等。 代码实现: ```cpp int gcd(int a, int b){ if(b==0) return a; return gcd(b,a%b); } ```