「作业」辗转相除法

A. 最大公约数和最小公倍数#

传送门:水滴

板子,虽然我已经能推出来了但还是要背一背(

点击查看代码
#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int maxn = 1e5 + 5;

ll a, b;

inline int gcd (int x, int y) {
    if(y == 0) return x;
    else return gcd(y, x % y);
}

inline int lcm (int x, int y) {
	return x * y / gcd(x, y);
}
int main(){
    scanf("%lld%lld", &a, &b);
    cout << gcd(a, b) << ' ' << lcm(a, b) << '\n';
    return 0;
}

B.最大公约数和最小公倍数问题#

传送门:水滴

题目大意:给出正整数 xy,找出 PQ 使 x 为它们的最大公约数,y 是它们的最小公倍数,询问有几组满足条件的 PQ

首先要知道一个公式(与本题无关):x×y=gcd(x,y)×lcm(x,y)

枚举 xy 的因数 1xy,如果满足 gcd(i,xyi)=x,则增加答案。因为只枚举了乘积的一半,所以最终结果要乘 2

x=y 时,会产生重复计算,所以要将 ans1

点击查看代码
#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int maxn = 1e5 + 5;

inline int gcd (int x, int y) {
	if (y == 0) return x;
	else return gcd(y, x % y);
}

inline int lcm (int x, int y) {
	return x * y / gcd(x, y);
}

int x, y;
int ans;

int main(){
    scanf("%d%d", &x, &y);
    for (int i = 1; i * i <= x * y; i++) {
    	if (x * y % i == 0 && gcd(i, x * y / i) == x)
    		ans ++;
    }
    ans *= 2;
    if (x == y) ans --;
    printf("%d", ans);
    return 0;
}

C.Hankson 的趣味题#

传送门:水滴

题目大意:给你正整数 a0,a1,b0,b1,询问你有多少个 x 满足 gcd(x,a0)=a1lcm(x,b0)=b1

首先考虑暴力,发现数据 2×109,否掉。考虑优化,首先联想到质数只需要枚举一半的数,那么再一次循环中可以用性质来得出对应的大数(即用小因数除出来),然后判断即可。考虑进一步优化,发现 b1 模上当前循环的数为 0 时才会对答案有贡献,所以当 b10 时才进行判断会进一步优化时间。时间复杂度是 O(n)

点击查看代码
#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int maxn = 1e5 + 5;

inline ll gcd (ll x, ll y) {
	if (y == 0) return x;
	else return gcd(y, x % y);
}

inline ll lcm (ll x, ll y) {
	return x * y / gcd(x, y);
}

int n;
ll a0, a1, b0, b1;

void solve() {
	int ans = 0;
	scanf("%lld%lld%lld%lld", &a0, &a1, &b0, &b1);
	for (int i = 1; i * i <= b1; i++) {
		if (b1 % i == 0) {
			if (gcd(i, a0) == a1 && lcm(i, b0) == b1)
				ans ++;
			int x = b1 / i;
			if (x != i && gcd(x, a0) == a1 && lcm(x, b0) == b1)
				ans ++;
		}
	}
	printf("%d\n", ans);
}

int main(){
    scanf("%d", &n);
    while (n--) solve();
    return 0;
}

D. 最大公约数#

传送门:水滴

题目大意:给定 n 个正整数。每次可以进行一次操作:将相邻的两个数中的一个变成这两个数的最大公约数,询问你需要几次可以把这 n 个数都变为 1

考虑暴力。首先推一遍样例,发现 1 是一个重要性质,可以去选择构造一个 1。当原数组中有 1 时,1 可以一个一个把周围的数变为 1。设 1 的数量是 cnt,则答案为 ncnt

考虑如果原数组中没有 1 的情况。设构造出一个 1 的次数是 sum,可以发现最后的答案就是 n+sum1

因为要找最小值,所以我们要找出最小的 sum,设它为 ans,答案就是 n+ans1。考虑如何遍历。发现可以 O(n) 扫一遍数组寻找一个点,暴力看这个点能在多少步往后 gcd 出来一个 1,步数为 sum。初始化时把 ans 设为无穷大。循环时找到 1anssummin,最后就能找到 ans

如果最后 ans 还是开始的无穷大,就说明没有解,输出 -1

点击查看代码
#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int maxn = 1e5 + 5;

inline int gcd (int x, int y) {
    if(y == 0) return x;
    else return gcd(y, x % y);
}

inline int lcm (int x, int y) {
	return x * y / gcd(x, y);
}

int n;
int a[maxn];
int ans = INT_MAX;

int main(){
    scanf("%d", &n);
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
    	scanf("%d", &a[i]);
    	if (a[i] == 1) cnt ++;
    }
    if (cnt) return printf("%d", n - cnt), 0;
    for (int i = 1; i < n; i++) {
    	int x = a[i];
    	int sum = 0;
    	for (int j = i + 1; j <= n; j++) {
    		x = gcd(x, a[j]), sum++;
    		if (x == 1) {
    			ans = min(ans, sum);
    			break;
    		}
    	}
    }
    if (ans == INT_MAX) printf("-1\n");
    else printf("%d", n + ans - 1);
    return 0;
}

发现 TLE 了一个点,所以考虑用数据结构优化。

作者:Aelt

出处:https://www.cnblogs.com/Aelt/p/18691826

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Aelt  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示