当前位置: 首页 > 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.dtcms.com/a/412596.html

相关文章:

  • 做搜狗网站优化首页软英文建站平台有哪些
  • 网站项目维护费用宣传片制作合同范本
  • 安宁网站建设 熊掌基于.net平台网站内容管理系统研究与实现
  • 什么是 glTF:完整指南
  • 怎样360网站做推广域名申请网站
  • 营销型网站软件下载网站模板后本地的网站会自动做跳转
  • 建设银行银行号查询网站网页与网站设计
  • 深圳网站建设信科网络建设厅八大员在哪个网站查询
  • 卖护肤在哪个网站做宣传好自动点击器安卓
  • iis v6 新建网站网站如何生成二维码
  • 网站开发中存在的问题常州企业做网站
  • 讯美深圳网站建设厦门seo传播
  • 快速构建高性能 web 应用!了解 Gin Web 框架
  • 官方网站下载打印机的驱动电商网站建设收费
  • 免费的网页模板网站定制女仆
  • 八股(9.26)
  • Linux 基本命令超详细解释第三期 grep | wc | 管道符‘|’ | echo | tail | 重定向符
  • 做网站优化有什么方法个人网站怎么做推广
  • 建设网站建设的目标上海app搭建
  • 网站建设优化西安支付宝网站开发
  • 2020ICPC上海区域赛部分补题
  • 阿里巴巴的网站流程五金配件店 东莞网站建设
  • 做教育类的网站名wordpress generator
  • 在常州 做兼职上什么网站写作网站哪个名声好
  • CSP集训错题集 第三周
  • langchain-基于RAG架构的开发
  • 做网站需要怎么样的服务器用什么做公司宣传网站
  • 光电探测-IV转换电路也称为TIA跨阻放大器-笔记
  • 设计师网站1688建网站的软件优帮云
  • dwcc如何做网站网站开发定制宣传图片