P8792 [蓝桥杯 2022 国 A] 最大公约数 题解
这里给出一个不一样的做法
(时间复杂度玄学,希望大佬帮忙分析一下 QAQ )
题外话(模拟赛上的题,但是脑子抽了没写出正解,就写了一个优雅的暴力,跑到了最优解 page 1 )
顺带一提(因为模拟赛上采用思路一的暴力过不去,但 lg 可以过,什么鬼数据,,所以跟大家分享一下思路二这种做法)
传送门
双倍经验(思路一的代码不加优化可以过,水蓝)
题意分析
这道题的操作其实就是求两个数的
思路(还有一种带log的做法其它大佬讲了我就不讲了)
一 考虑暴力(其实只要在原暴力上加个优化就过了)
我们知道
证明
那么我们对于每一个
以上面的思路,我们枚举
当然,我们要特判,如果原序列中有了
期望得分
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+100;
int n,a[N],res;
main(){
cin>>n;
for (int i=1;i<=n;i++) cin>>a[i],res+=(a[i]==1?1:0);
if (res){
cout<<n-res;
return 0;
}
int step=1e9;
for (int i=1;i<n;i++)
{
int x=a[i];
for (int j=i+1;j<=n;j++)
{
x=__gcd(x,a[j]);//求gcd的自带函数
if(j-i>step) break;//优化
if(x==1)
{
step=j-i;
break;
}
}
}
if (step==1e9) cout<<-1;
else cout<<n-1+step;
return 0;
}
二 还是考虑暴力
我们考虑对思路一进行优化
我们可以发现,思路一中造成超时的主要原因是因为我们没法保证每次得出的
但是如果我们能保证第一次得出的答案即为最优,那么嘿嘿嘿(这里说一下,其实多数题目可以这样思考,因为出题人是知道朴素暴力怎么卡的,所以他肯定会卡,而我们只要绕开这点,就可以让出题人的卡暴力成为我们
好了好了,进入正题
我们先借用一篇巨佬的
here
我建议大家使用第四种,因为可以处理
紧接着我们回忆一下思路一,其中的
一共可以得出
那我们要求的不就是一个连续区间使一个区间所有数的
那我们只要从区间长度入手,每次枚举出长度为
对一些可能的疑惑的解答(为什么只找一个):当然,我们只需要一个连续区间的
我们接着思考,长度为
上图以
具体实现:我们新建一个
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
//#define int long long
#define re register
using namespace std;
const int N=1e5+110;
int n,a[N],num,res;
bool f;
int b[N],tot,k;
inline int read()
{
int ress=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ress=(ress<<3)+(ress<<1)+c-'0',c=getchar();
return ress;
}
inline int gcd_(int x2,int y2)
{
int x=max(x2,y2),y=min(x2,y2);
if(y) while((x%=y)&&(y%=x)) ;
return x+y;
}
signed main()
{
n=read();
for(re int i=1;i<=n;++i)
{
b[i]=a[i]=read();
if(a[i]==1) ++num;
}
if(num) {printf("%d",n-num);return 0;}//特判,前面思路一讲过
tot=n;
if(n==1) return printf("-1"),0;
int gg=gcd_(a[1],a[2]);
for(re int i=3;i<=n;++i)gg=gcd_(gg,a[i]);
//这个特判可以加可以不加,因为模拟赛的时候后面几个大的数据点是-1,如果最后判断的话可以卡成n^2的
if(gg>1) return printf("-1"),0;
while(!f&&k<n)
{
++k;//迭代层数
--tot;//当前b数组中数的个数
for(re int i=1;i<=tot;++i)
{
b[i]=gcd_(b[i],b[i+1]);
if(b[i]==1)
{
f=1;
break ;
}
}
if(f) break ;
}
if(!f) printf("-1");
else printf("%d",n-1+k);
return 0;
}
至此,本题解决,在
最后,我再来说一个小优化思路(可以在迭代