当前位置: 首页 > news >正文

GESP C++5级 2025年6月编程2题解:最大公因数 - 教程

P13014 [GESP202506 五级] 最大公因数

题目描述

对于两个正整数 a,ba,ba,b,他们的最大公因数记为 gcd⁡(a,b)\gcd(a,b)gcd(a,b)。对于 k>3k > 3k>3 个正整数 c1,c2,…,ckc_1,c_2,\dots,c_kc1,c2,,ck,他们的最大公因数为:

gcd⁡(c1,c2,…,ck)=gcd⁡(gcd⁡(c1,c2,…,ck−1),ck)\gcd(c_1,c_2,\dots,c_k)=\gcd(\gcd(c_1,c_2,\dots,c_{k-1}),c_k)gcd(c1,c2,,ck)=gcd(gcd(c1,c2,,ck1),ck)

给定 nnn 个正整数 a1,a2,…,ana_1,a_2,\dots,a_na1,a2,,an 以及 qqq 组询问。对于第 i(1≤i≤q)i(1 \le i \le q)i(1iq) 组询问,请求出 a1+i,a2+i,…,an+ia_1+i,a_2+i,\dots,a_n+ia1+i,a2+i,,an+i 的最大公因数,也即 gcd⁡(a1+i,a2+i,…,an+i)\gcd(a_1+i,a_2+i,\dots,a_n+i)gcd(a1+i,a2+i,,an+i)

输入格式

第一行,两个正整数 n,qn,qn,q,分别表示给定正整数的数量,以及询问组数。

第二行,nnn 个正整数 a1,a2,…,ana_1,a_2,\dots,a_na1,a2,,an

输出格式

输出共 qqq 行,第 iii 行包含一个正整数,表示 a1+i,a2+i,…,an+ia_1+i,a_2+i,\dots,a_n+ia1+i,a2+i,,an+i 的最大公因数。

输入输出样例 #1

输入 #1

5 3
6 9 12 18 30

输出 #1

1
1
3

输入输出样例 #2

输入 #2

3 5
31 47 59

输出 #2

4
1
2
1
4

说明/提示

对于 60%60\%60% 的测试点,保证 1≤n≤1031 \le n \le 10^31n1031≤q≤101 \le q \le 101q10

对于所有测试点,保证 1≤n≤1051 \le n \le 10^51n1051≤q≤1051 \le q \le 10^51q1051≤ai≤10001 \le a_i \le 10001ai1000

题目大意

给定nnn个正整数组成的序列aaaqqq次询问,对于第i(1≤i≤q)i(1\le i\le q)i(1iq)次询问,输出gcd⁡(a1+i,a2+i,…,an+i)\gcd(a_1+i,a_2+i,\dots,a_n+i)gcd(a1+i,a2+i,,an+i)的值。

题目分析

先说TLE的解法。
最简单的办法就是在输入后直接一次一次反复求整个数组加iii的最大公因数,意思就是直接处理每次询问,每次询问都遍历数组且求一遍gcd⁡\gcdgcd,这是很多考生都用的办法。

欧几里得算法模板

欧几里得算法(辗转相除法)求最大公因数可以用下面的精简代码

int gcd(int x,int y)
{
return y==0?x:gcd(y,x%y);
}

也可以使用迭代

int gcd(int x,int y)
{
while(y!=0)
{
int tmp=y;
y=x%y;
x=tmp;
}
return x;
}

复杂度如下表

递归迭代
时间复杂度O(log⁡n)O(\log n)O(logn)O(log⁡n)O(\log n)O(logn)
空间复杂度O(log⁡n)O(\log n)O(logn)O(1)O(1)O(1)

递归的空间用于递归栈调用,该题中无需考虑。

TLE Code

#include<bits/stdc++.h>using namespace std;int a[100001];int gcd(int x,int y){return y==0?x:gcd(y,x%y);}int main(){int n,q;cin>>n>>q;for(int i=1;i<=n;i++)cin>>a[i];for(int i=1;i<=q;i++){int g=a[1]+i;for(int j=2;j<=n;j++)g=gcd(g,a[j]+i);cout<<g<<endl;}return 0;}

在这里插入图片描述
但是一旦提交
在这里插入图片描述
就噶了。

TLE Time

输入比处理快,只看处理部分。
外重循环:O(q)O(q)O(q)
内重循环:O(n)O(n)O(n)
gcd⁡\gcdgcdO(log⁡ai)O(\log a_i)O(logai)
总时间:O(nqlog⁡ai)O(nq\log a_i)O(nqlogai),数据范围不让过。
那过的方法是什么呢?

差分数组

对于长度为nnn的序列aaa,他的差分数组第一项与原数组第一项相等,后面差分数组第i(2≤i≤n)i(2\le i\le n)i(2in)项等于ai−ai−1a_i-a_{i-1}aiai1,注意即使是负数也不能绝对值。
现在,我要证明一个东西:gcd⁡(a+c,b+c)=gcd⁡(a+c,b−a)\gcd(a+c,b+c)=\gcd(a+c,b-a)gcd(a+c,b+c)=gcd(a+c,ba)

证明gcd⁡(a+c,b+c)=gcd⁡(a+c,b−a)\gcd(a+c,b+c)=\gcd(a+c,b-a)gcd(a+c,b+c)=gcd(a+c,ba)

众所周知,辗转相除法一开始是辗转相减法,后来通过优化才变成辗转相除法。
后面的取模比之前的相减快多了,但是不要忘本。
对于gcd(a,b),可以推出gcd(b,a-b),他们是等价的,你可以想象成在一个大长方形里面取截取最大的正方形,剩下的长宽就是gcd的参数。
那么用于gcd⁡(a+c,b+c)=gcd⁡(a+c,b−a)\gcd(a+c,b+c)=\gcd(a+c,b-a)gcd(a+c,b+c)=gcd(a+c,ba)也好使,就假设a+ca+ca+ca′a'ab+cb+cb+cb′b'b
gcd⁡(a′,b′)=gcd⁡(a′,b−a)\gcd(a',b')=\gcd(a',b-a)gcd(a,b)=gcd(a,ba)。你感觉不一样。
来,别急,aaabbb的大小关系是要调整的,gcd⁡(a′,b−a)\gcd(a',b-a)gcd(a,ba)变成gcd⁡(b′,a−b)\gcd(b',a-b)gcd(b,ab),是不是恍然大悟了?
没错,跟辗转相减法几乎一样了。其实这个式子是gcd⁡(a,b)\gcd(a,b)gcd(a,b)演变而来,他就是把a,ba,ba,b都加上ccc,证明新aaaa,ba,ba,b差的最大公因数就是新的两个数的最大公因数。
这样,就能在一串数中,整体递增相同元素后快速找到最大公因数。
没听懂证明过程?举个栗子!

举个栗子

例如3 7c=1c=1c=1gcd⁡(a+c,b+c)=gcd⁡(4,8)=4\gcd(a+c,b+c)=\gcd(4,8)=4gcd(a+c,b+c)=gcd(4,8)=4gcd⁡(a+c,b−a)=gcd⁡(4,4)=4\gcd(a+c,b-a)=\gcd(4,4)=4gcd(a+c,ba)=gcd(4,4)=4,确实相等。
再比如5 6c=2c=2c=2gcd⁡(a+c,b+c)=gcd⁡(7,8)=1\gcd(a+c,b+c)=\gcd(7,8)=1gcd(a+c,b+c)=gcd(7,8)=1gcd⁡(a+c,b−a)=gcd⁡(7,1)=1\gcd(a+c,b-a)=\gcd(7,1)=1gcd(a+c,ba)=gcd(7,1)=1,确实相等。
其实这是必然的,自己随便写几个样例都是的。

实际应用

在一串数中,求完差分数组,把他从第二项起,每一项都取最大公因数。最后在每次询问中,用任意一个元素加上iii再与前面的gcd⁡\gcdgcd结果再一次进行gcd⁡\gcdgcd,得到的结果就是输出的答案。

Code

#include<bits/stdc++.h>using namespace std;int a[100001],cf[100001]; //原数组和差分数组int gcd(int x,int y) //最大公因数{return y==0:x:gcd(y,x%y);}int main(){int n,q;cin>>n>>q; //输入for(int i=1;i<=n;i++)cin>>a[i],cf[i]=a[i]-a[i-1]; //输入 计算差分int g=cf[2];for(int i=3;i<=n;i++)g=gcd(g,cf[i]); //求gcdfor(int i=1;i<=q;i++){cout<<gcd(a[1]+i,g)<<endl; //处理 输出}return 0;}

在这里插入图片描述
然而提交的时候
在这里插入图片描述
没错,n=1n=1n=1时忘了特判,加了特判能AC。

#include<bits/stdc++.h>using namespace std;int a[100001],cf[100001]; //原数组和差分数组int gcd(int x,int y) //最大公因数{return y==0?x:gcd(y,x%y);}int main(){int n,q;cin>>n>>q; //输入for(int i=1;i<=n;i++)cin>>a[i],cf[i]=a[i]-a[i-1]; //输入 计算差分if(n==1){for(int i=1;i<=q;i++)cout<<a[1]+i<<endl;return 0;}int g=cf[2];for(int i=3;i<=n;i++)g=gcd(g,cf[i]); //求gcdfor(int i=1;i<=q;i++){cout<<gcd(a[1]+i,g)<<endl; //处理 输出}return 0;}

在这里插入图片描述
…个毛啊!
等一下,0分!?自定义评分脚本这么BT吗?
你一不小心把鼠标放到了wa的点上。
在这里插入图片描述
翻译:第3行第一列有字符错误,读取到了-,正确应是1
哦哦哦!忘记处理负数,加个abs就好了!

#include<bits/stdc++.h>using namespace std;int a[100001],cf[100001]; //原数组和差分数组int gcd(int x,int y) //最大公因数{return y==0?x:gcd(y,x%y);}int main(){int n,q;cin>>n>>q; //输入for(int i=1;i<=n;i++)cin>>a[i],cf[i]=a[i]-a[i-1]; //输入 计算差分if(n==1){for(int i=1;i<=q;i++)cout<<a[1]+i<<endl;return 0;}int g=cf[2];for(int i=3;i<=n;i++)g=gcd(g,cf[i]); //求gcdfor(int i=1;i<=q;i++){cout<<abs(gcd(a[1]+i,g))<<endl; //处理 输出}return 0;}

在这里插入图片描述
啊!终于AC了。

总结

本题对于不会数学的人来说,送你一个词:天方夜谭。
对于只会简单gcd而不知道其中原理而tle的人,送你一个词:一瓶子不满半瓶子晃。
只有会进行细致数学分析和耐心查错的人,才能AC。
为什么5级都是这种数学分析题啊?

http://www.jsqmd.com/news/11980/

相关文章:

  • 阿里发布「夸克 AI 眼镜」:融合阿里购物、地图、支付生态;苹果拟收购计算机视觉初创 Prompt AI丨日报
  • 在AI技术唾手可得的时代,挖掘新需求成为制胜关键——某知名AI聊天框架需求探索
  • 数论学习之路
  • CSharp: Aspose.Cells 8.3.0 web excel Viewer
  • 详细介绍:C# WinForms的入门级画板实现
  • 【springboot的分页功能TableDataInfo,有时候需要艰难的分页实现,怎么办呢?】
  • 生成式AI实现多模态信息检索技术突破
  • 在运维工作中,如何过滤某个目录在那边什么路径下面?
  • 完整教程:安卓中,kotlin如何写app界面?
  • 移动固态硬盘插入电脑后提示“应该格式化”或“文件系统损坏”如何修复?
  • PHP 15 个高效开发的小技巧
  • AI元人文构想研究:人类拥抱AI的文明新范式
  • 华为发布星河AI广域网解决方案,四大核心能力支撑确定性网络 - 详解
  • 【汇编】汇编语言运行过程
  • 设计模式与原则精要 - 详解
  • 电感式传感器 - 实践
  • CSP-J/S2024第二轮提高级题目知识构成分析报告
  • 浅层 CNN 的瓶颈:用 LeNet 实测不同数据集
  • lCode题库
  • Arista cEOS 4.35.0F 发布 - 针对云原生环境设计的容器化网络操作系统
  • Arista vEOS 4.35.0F 发布 - 虚拟化的数据中心和云网络可扩展操作系统
  • 因果机器学习的技术发展与挑战
  • 深入解析:Spring依赖注入方式
  • CSP-S 考前集训
  • 通过rqlite sdk 快速访问sqlite-vec
  • CSharp: itextsharp5 imge converter pdf
  • 20251011 总结
  • 上课讲的部分 qoj 题记录
  • var与let
  • CSP-S 第二轮集训资料 **总结 + 专题细分精讲**_from_黄老师