最大公约数的英文是Greatest Common Divisor,常缩写为 gcd。
我们首先先了解一下约数的概念:如果存在一个整数 k k k,使得 a = k d a=kd a=kd ,则 d d d称 a a a整除,记做 d ∣ a d|a d∣a,称 a a a是 d d d的倍数,如果 d > 0 d>0 d>0,称 d d d是 a a a的约数。特别地,任何整数都整除 0 0 0。
那什么是公约数?
一组数的公约数,是指同时是这组数中每一个数的约数的数。而最大公约数,则是指所有公约数里面最大的一个。
那又有了一个问题了,我们整么求出一组数的最大公约数呢?
我们先考虑两个数的情况。
我们已知两个数 a a a和 b b b,其中 a > b a>b a>b。
如果 b b b是 a a a的约数,那么最大公约数就是 b b b。
如果不能整除,那么 a = b ∗ k + r a=b*k+r a=b∗k+r,其中 r < b rr<b。
我们最后其实可以得到 g c d ( a , b ) = g c d ( b , b m o d a ) gcd(a,b)=gcd(b,b\mod\ a) gcd(a,b)=gcd(b,bmod a)。
设 a = b k + c a=bk+c a=bk+c,那么有 c = a m o d b c=a\mod\ b c=amod b。设 d ∣ a d ∣ b d|a\ \ \ d|b d∣a d∣b,则 c − a − b k c d = a d − b d k c-a-bk \frac{c}{d}=\frac{a}{d}-\frac{b}{d}k c−a−bkdc=da−dbk由右边式子可知 c d \frac{c}{d} dc为整数,即 d ∣ c d|c d∣c所以对于 a , b a,b a,b的公约数,它也会是 a m o d b a\mod\ b amod b的公约数。
反过来也需要证明。
设 d ∣ b d ∣ ( a m o d b ) d|b\ \ \ \ d|(a\mod\ b) d∣b d∣(amod b),我们还是可以像之前一样得到以下式子 a m o d b d = a d − b d k \frac{a\mod\ b}{d}=\frac{a}{d}- \frac{b}{d}k damod b=da−dbk, a m o d b d + b d k = a d \frac{a\mod\ b}{d}+\frac{b}{d}k=\frac{a}{d} damod b+dbk=da因为左边式子显然为整数,所以 a d \frac{a}{d} da也为整数,即 d ∣ a d|a d∣a,所以 b , a m o d b b,a\mod\ b b,amod b的公约数也是 a , b a,b a,b的公约数。
既然两式公约数都是相同的,那么最大公约数也会相同。
得证。
既然如此,我们得到一种求最大公约数的方法:
int gcd(int a,int b) {
return b?gcd(b,a%b):a;
}
其时间复杂度为 O ( l o g ( a + b ) ) O(log(a+b)) O(log(a+b))。
最小公倍数的英文是Least Common Multiple,常缩写为LCM。
公倍数的概念是指同时是这组数中每一个数的倍数。而最小公倍数,则是指所有公倍数里面最小的一个。没有最大公倍数。
我们又怎么求最小公倍数呢?我们目前可以想到的唯有暴力。
当然,也一个牛(e)逼(xin)方法:
首先,我们有两个数: x x x和 y y y。
a = p 1 k a 1 p 2 k a 2 ⋯ p s k a s , b = p 1 k b 1 p 2 k b 2 ⋯ p s k b s a = p_1^{k_{a_1}}p_2^{k_{a_2}} \cdots p_s^{k_{a_s}},b = p_1^{k_{b_1}}p_2^{k_{b_2}} \cdots p_s^{k_{b_s}} a=p1ka1p2ka2⋯pskas,b=p1kb1p2kb2⋯pskbs
那么,
g c d = p 1 m i n ( k a 1 , k b 1 ) p 2 m i n ( k a 2 , k b 2 ) ⋯ p s m i n ( k a s , k b s ) gcd=p_1^{min(k_{a_1},k_{b_1})}p_2^{min(k_{a_2},k_{b_2})} \cdots p_s^{min(k_{a_s},k_{b_s})} gcd=p1min(ka1,kb1)p2min(ka2,kb2)⋯psmin(kas,kbs)
L C M = p 1 m a x ( k a 1 , k b 1 ) p 2 m a x ( k a 2 , k b 2 ) ⋯ p s m a x ( k a s , k b s ) LCM=p_1^{max(k_{a_1},k_{b_1})}p_2^{max(k_{a_2},k_{b_2})} \cdots p_s^{max(k_{a_s},k_{b_s})} LCM=p1max(ka1,kb1)p2max(ka2,kb2)⋯psmax(kas,kbs)
我们可以发现如下结论: a × b = g c d × L C M a\times b=gcd\times LCM a×b=gcd×LCM
于是,我们就有了一种求最小公倍数的简便方法: L C M = a × b ÷ g c d LCM=a\times b\div gcd LCM=a×b÷gcd
题目传送门
题目描述
输入两个正整数 x 0 , y 0 x_0,y_0 x0,y0,求出满足下列条件的 P , Q P,Q P,Q的个数:
1. P , Q P,Q P,Q 是正整数。
2.要求 P , Q P,Q P,Q以 x 0 x_0 x0为最大公约数,以 y 0 y_0 y0为最小公倍数。
试求:满足条件的所有可能的 P , Q P,Q P,Q的个数。
输入格式
一行两个正整数 x 0 , y 0 x_0, y_0 x0,y0
输出格式
一行一个数,表示求出满足条件的 P , Q P, Q P,Q的个数。
样例
输入: 输出:
3 60 4
对于100%的数据, 2 ≤ x 0 , y 0 ≤ 1 0 5 2\le x_0,y_0\le 10^5 2≤x0,y0≤105
我们之前说过,两个数的积等于它们最大公约数和它们最小公倍数的积。所以满足条件的两个数的积一定是读入的两个数的积。所以我们就可以枚举一个数,判断它能否被读入的两个数的积整除,如果可以再判断它和两个数的积除以它所得的数的最大公约数和读入的最大公约数是否相同,如果相同则ans++,(因为积相同而且最大公约数相同,那么最小公倍数也一定相同)最后输出ans。但是,会超时。
于是我们要想办法优化。由样例可知,3,60和60,3算不同情况。所以每组数都有一个与之相反的答案,因此从1到两数积的根号枚举一遍,每发现一组答案ans就加上2即可。
#include
using namespace std;
int x,y,k,ans;
bool l;
int gcd(int a,int b) {
return b?gcd(b,a%b):a;
}
int main() {
scanf("%d %d",&x,&y);
k=x*y;
for(int i=1;i*i<=k;i++) {
if(k%i==0 && gcd(i,k/i)==x) {
ans++;
if(i*i==k) l=1;//如果不这样的话,i*i的情况会重复一次
}
}
printf("%d",ans*2-l);//最后乘以二是因为只遍历了一半
return 0;
}
题目传送门
题目描述
为了把毕业晚会办得更好,老师想要挑出默契程度最大的 k k k个人参与毕业晚会彩排。可是如何挑呢?老师列出全班同学的号数 1 , 2 , ⋯ , n 1,2,\cdots,n 1,2,⋯,n并且相信 k k k个人的默契程度便是他们的最大公约数(这不是迷信哦~)。这可难为了他,请你帮帮忙吧!
PS:一个数的最大公约数即本身。
输入格式
两个空格分开的正整数 n n n和 k k k
输出格式
一个整数,为最大的默契值。
样例
输入: 输出:
4 2 2
对于100%的数据, k ≤ 1 0 9 , n ≤ 1 0 9 , n ≤ k ≤ 1 k\le 10^9,n\le 10^9,n\le k\le 1 k≤109,n≤109,n≤k≤1
求:从 1 ∼ n 1\sim n 1∼n中取 k k k个数,使这 k k k个数的最大公约数最大
因为两个数成倍数关系时,它们的最大公因数是两数中的较小数,也就是相对来说最大公因数较大
返回题目,这k个数其实就是x的1~k倍,但必须保证x * k小于n,在上述条件下,能知道,符合条件的最大的x就是答案,为了找出最大的x,必须使x * k尽量接近n,因为c++的整数除法有自动取整的功能,所以所有情况下,n/k都是最终答案。
#include
using namespace std;
int n,k;
int main() {
scanf("%d %d",&n,&k);
printf("%d",n/k);
return 0;
}
题目传送门
题目描述
彩排了一次,老师不太满意。当然啦,取每位同学的号数来找最大公约数显然不太合理。于是老师给每位同学评了一个能力值。于是现在问题变为,从n个学生中挑出k个人使得他们的默契程度(即能力值的最大公约数)最大。但因为节目太多了,而且每个节目需要的人数又不知道。老师想要知道所有情况下能达到的最大默契程度是多少。这下子更麻烦了,还是交给你吧~
PS:一个数的最大公约数即本身。
输入格式
第一行一个正整数 n n n。
第二行为 n n n个空格隔开的正整数,表示每个学生的能力值。
输出格式
总共n行,第i行为k=i情况下的最大默契程度。
样例
输入: 输出:
4 4
1 2 3 4 2
1
1
对于100%的数据, n ≤ 10000 , i n f ≤ 1 e 6 n\le10000,inf\le1e6 n≤10000,inf≤1e6
k个数的公约数含义就是这k个数均含有某个因数,如果我们把所有数的因数全部求出来,发现有k个数均含有某个因数,那么这个数必然是这k个数的公约数。其中找出最大的就是它们的最大公约数。
每个数分解因数,c[i]表示i作为因子的次数。 对于答案i,c[x]>i的p可以作为答案。
可以发现i的答案一定大于等于i+1的答案。
扫一遍找答案行了。
#include
using namespace std;
int n,maxx,a[10000005];
int main() {
scanf("%d",&n);
for(int i=1,x;i<=n;i++) {
scanf("%d",&x);
maxx=max(x,maxx);
for(int j=1;j*j<=x;j++) {
if(x%j==0) {
a[j]++;
if(j*j!=x) a[x/j]++;
}
}
}
int x=maxx;
for(int i=1;i<=n;i++) {
while(a[x]