蓝桥杯2022年第十三届决赛真题-最大公约数(C/C++/Java组)

题目描述

给定一个数组,每次操作可以选择数组中任意两个相邻的元素 x, y 并将其中的一个元素替换为 gcd(x, y) ,其中 gcd(x, y) 表示 x 和 y 的最大公约数。

请问最少需要多少次操作才能让整个数组只含 1 。

输入格式

输入的第一行包含一个整数 n ,表示数组长度。

第二行包含 n 个整数 a1, a2, · · · , an,相邻两个整数之间用一个空格分隔。

输出格式

输出一行包含一个整数,表示最少操作次数。如果无论怎么操作都无法满足要求,输出 −1 。
样例输入

3
4 6 9

样例输出

4

提示

对于 30% 的评测用例,n ≤ 500 ,ai ≤ 1000;

对于 50% 的评测用例,n ≤ 5000 ,ai ≤ 1 0 6 10^6 106

对于所有评测用例,1 ≤ n ≤ 100000 ,1 ≤ ai ≤ 1 0 9 10^9 109

题目链接

思路 or 题解

性质 or 结论: g c d ( 1 , x ) = 1 gcd(1, x) = 1 gcd(1,x)=1

我们需要找最小次数去得到 1 1 1
1 1 1 之后顺着就可以把所有数变成 1 1 1

我们可以通过线段树 维护区间 GCD
然后通过二分求解最小次数

AC 代码:

/*
Make it simple and keep self stupid
author:Joanh_Lan
*/
#pragma GCC optimize(3)
#pragma GCC optimize("inline") // 如果比赛允许开编译器优化的话,可以默写这两段
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <numeric>
#include <cstring>
#include <cmath>
#include <map>
#include <unordered_map>
#include <bitset>
#include <set>
#include <random>
#include <ctime>
#include <queue>
#include <stack>
#include <climits>
#define buff                     \
    ios::sync_with_stdio(false); \
    cin.tie(0);
// #define int long long
#define ll long long
#define PII pair<int, int>
#define px first
#define py second
typedef std::mt19937 Random_mt19937;
Random_mt19937 rnd(time(0));
using namespace std;
const int mod = 1e9 + 7;
const int inf = 2147483647;
const int N = 100009;
//int Mod(int a,int mod){return (a%mod+mod)%mod;}
//int lowbit(int x){return x&-x;}//最低位1及其后面的0构成的数值
//int qmi(int a, int k, int p){int res = 1 % p;while (k){if (k & 1) res = Mod(res * a , p);a = Mod(a * a , p);k >>= 1;}return res;}
//int inv(int a,int mod){return qmi(a,mod-2,mod);}
//int lcm(int a,int b){return a*b/__gcd(a,b);}
int n, a[N], tr[N << 2];
void bulid(int l, int r, int p)
{
	if (l == r)
	{
		tr[p] = a[l];
		return;
	}
	int mid = l + (r - l >> 1);
	bulid(l, mid, p << 1);
	bulid(mid + 1, r, p << 1 | 1);
	tr[p] = __gcd(tr[p << 1], tr[p << 1 | 1]);
}
int query(int l, int r, int s, int t, int p)
{
	int ans = 0;
	if (l >= s && r <= t)	return tr[p];
	int mid = l + (r - l >> 1);
	if (mid >= s)
		ans = __gcd(ans, query(l, mid, s, t, p << 1));
	if (mid < t)
		ans = __gcd(ans, query(mid + 1, r, s, t, p << 1 | 1));
	return ans;
}
void solve()
{
	cin >> n;
	int cnt_one = 0;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		if (a[i] == 1)	cnt_one++;
	}
	if (cnt_one)
	{
		cout << n - cnt_one << '\n';
		return;
	}
	bulid(1, n, 1);
	int ans = inf;
	for (int i = 1; i <= n; i++)
	{
		int l = i, r = n;
		int cnt = inf;
		while (l <= r)
		{
			int mid = l + (r - l >> 1);
			if (query(1, n, i, mid, 1) == 1)
				cnt = mid - i, r = mid - 1;
			else 
				l = mid + 1;
		}
		ans = min(ans, cnt);
	}
	// cout << ans << '\n';
	if (ans == inf)
	{
		cout << -1 << '\n';
		return;
	}
	ans = ans + n - 1;
	cout << ans << '\n';
}
int main()
{
	buff;
	int _ = 1;
	// cin >> _;
	while (_--)
		solve();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Joanh_Lan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值