Codeforces Round #641 (Div. 2) 参与排名人数11937
[codeforces 1350C] Orac and LCM 最大公约数+最小公倍数+公式推导
总目录详见https://blog.csdn.net/mrcrack/article/details/103564004
在线测评地址http://codeforces.com/contest/1350/problem/C
Problem | Lang | Verdict | Time | Memory |
---|---|---|---|---|
C - Orac and LCM | GNU C++17 | Accepted | 46 ms | 6000 KB |
数论中,一个很简单的公式,若不知道的情况下,真是弄得够呛。
通过打比赛,了解数论中的公式,也是一个不错的选择。
公式推导如下
a1 a2 a3 a4
a1*a2代表LCM(a1,a2)
GCD(a1*a2,a1*a3,a1*a4)
=a1*GCD(a2,a3,a4)
即
求a1与GCD(a2,a3,a4)的最小公倍数c1
GCD(a2*a3,a2*a4)
=a2*GCD(a3,a4)
即
求a2与GCD(a3,a4)的最小公倍数c2
GCD(a3*a4)
=a3*GCD(a4)
即
求a3与GCD(a4)的最小公倍数c3
最后再求GCD(c1,c2,c3)
证明如下(以下证明,若读者看不下去,可以直接跳到AC代码):
目标是证明:GCD(LCM(a1,a2),LCM(a1,a3),LCM(a1,a4))==LCM(a1*GCD(a2,a3,a4))
a1 a2 a3 a4
a1*a2代表LCM(a1,a2)
GCD(a1*a2,a1*a3,a1*a4)
=a1*GCD(a2,a3,a4)
即
求a1与GCD(a2,a3,a4)的最小公倍数c1
请注意:下面证明过程中的*仅代表乘号,重要的事情说三遍,*仅代表乘号,*仅代表乘号。
证明过程如下:
LCM(a1,a2)=a1*k2*d,a2=a12*k2*d,注意a12是a1里面的因子,
LCM(a1,a3)=a1*k3*d,a3=a13*k3*d,注意a13是a1里面的因子,
LCM(a1,a4)=a1*k4*d,a4=a14*k4*d,注意a14是a1里面的因子,
请注意,GCD(k2,k3,k4)=1
GCD(LCM(a1,a2),LCM(a1,a3),LCM(a1,a4))
=GCD(a1*k2*d,a1*k3*d,a1*k4*d)
=a1*d*GCD(k2,k3,k4)
因GCD(k2,k3,k4)=1
上式变为a1*d
LCM(a1*GCD(a2,a3,a4))
=LCM(a1*GCD(a12*k2*d,a13*k3*d,a14*k4*d)
=LCM(a1*d*GCD(a12*k2,a13*k3,a14*k4)
因GCD(k2,k3,k4)=1
GCD(k2,a13)==a1中的因子
GCD(k2,a14)==a1中的因子
上式变为
LCM(a1*d*GCD(a12,a13*k3,a14*k4))
因GCD(k2,k3,k4)=1
GCD(k3,a12)==a1中的因子
GCD(k3,a14)==a1中的因子
上式变为
LCM(a1*d*GCD(a12,a13,a14*k4))
因GCD(k2,k3,k4)=1
GCD(k4,a12)==a1中的因子
GCD(k3,a13)==a1中的因子
上式变为
LCM(a1*d*GCD(a12,a13,a14))
请注意,因为a12是a1里面的因子,a13是a1里面的因子,a14是a1里面的因子
故
LCM(a1*GCD(a12,a13,a14))=a1
因此式子LCM(a1*d*GCD(a12,a13,a14))变为LCM(a1*d)=a1*d
故
GCD(LCM(a1,a2),LCM(a1,a3),LCM(a1,a4))==
LCM(a1*GCD(a2,a3,a4))
结论得证。
AC代码如下
#include
#define LL long long
#define maxn 100010
LL a[maxn],b[maxn],c[maxn],ans;
LL GCD(LL a,LL b){//最大公约数
return b?GCD(b,a%b):a;
}
LL LCM(LL a,LL b){//最小公倍数
return a*b/GCD(a,b);
}
int main(){
int n,i;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%lld",&a[i]);
b[n]=a[n];
for(i=n-1;i>=1;i--)b[i]=GCD(b[i+1],a[i]);//b[1]=GCD(a1,a2,a3,......,an)
for(i=1;i
方法二:根据质因数出现频率找留存质因数
Problem | Lang | Verdict | Time | Memory |
---|---|---|---|---|
C - Orac and LCM | GNU C++17 | Accepted | 140 ms | 10100 KB |
样例模拟如下
Input:
4
10 24 40 80
10=2*5
24=3*2^3
40=5*2^3
80=5*2^4
可以看到4个数中,因数5出现4-1=3次,因数5要计入结果
可以看到4个数中,因数2^3出现4-1=3次,因数2^3要计入结果
可以看到4个数中,因数3出现2次,达不到4-1=3次,因数3只好扔了
故,输出结果为5*2^3=40
Output:
40=5*2^3
AC代码如下(因C语言的写法要爆内存,引入了STL中的vector,第一次用。)
#include
#include
#include
#define LL long long
using namespace std;
vector vec[20000];//20000的原因是,在[2,200000]之间质数个数不会超过18000,这可通过线性筛素数得到
int not_prime[200010],prime[20000],pn,x,a[100010],pos[200010];
void linear_shaker(int x){//线性筛素数
int i,j;
for(i=2;i<=x;i++){
if(!not_prime[i])pn++,prime[pn]=i,pos[i]=pn;//pos[i]=pn标记质数i在prime[]中的位置
for(j=1;i*prime[j]<=x;j++){
not_prime[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
void divide(int x){//将x进行质因数分解
int i;
for(i=2;i*i<=x;i++)
if(x%i==0){
int cnt=0;//cnt不会超过20,理由:2^20=10^6>200000
while(x%i==0)x/=i,cnt++;//cnt统计i的幂次
vec[pos[i]].push_back(cnt);//引入vec[]实属无奈,考虑了半天,C语言要爆内存,暂时找不到好办法。
}
if(x>1)vec[pos[x]].push_back(1);//x>1表示该数是素数
}
int main(){
int n,i,j;
LL ans=1;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]),x=max(x,a[i]);
linear_shaker(x);
for(i=1;i<=n;i++)divide(a[i]);
for(i=1;i<=pn;i++)
if(vec[i].size()==n-1){//若有n-1个数有雷同的质因数
sort(vec[i].begin(),vec[i].end());
for(j=1;j<=vec[i][0];j++)ans*=prime[i];//取最小幂次vec[i][0]
}else if(vec[i].size()==n){//若有n个数有雷同的质因数
sort(vec[i].begin(),vec[i].end());
for(j=1;j<=vec[i][1];j++)ans*=prime[i];//取第二小幂次vec[i][0]
}
printf("%lld\n",ans);
return 0;
}