P1851 好朋友

记录28

#include <bits/stdc++.h>
using namespace std;
int f(int x){
	int sum=0; 
	for(int i=1;i<x;i++){
		if(x%i==0) sum+=i; 
	} 
	return sum;
}
int main(){
	int s;
	cin>>s;
	for(s;;s++){
		if(f(f(s))==s&&s!=f(s)){
			cout<<s<<" "<<f(s);
			break; 
		}
	}
    return 0;
}

突破点

如果 A 的序列号的约数之和恰好等于 B 的序列号,那么 A 的好朋友就是 B。

👉一个数的约数和等于另一个数

在这里,一个数的约数不包括这个数本身👉没说不包括1

当两个同学互为“好朋友”时,他们就是一对“非常好友” 👉两个数的约数和互为对方

给定一个序列号 s,找出序列号不小于 s 的第一对“非常好友”

👉从输入的数开始找两个约数和互为对方的数


思路

  1. 写一个求约数和的函数
  2. 枚举数字,找到后输出

代码简析

int f(int x){
	int sum=0; 
	for(int i=1;i<x;i++){
		if(x%i==0) sum+=i; 
	} 
	return sum;
}

对一个数求约数,就是对这个数取余为0

注意:题目只说不包括自身,记得从一开始

#include <bits/stdc++.h>
using namespace std;
int f(int x){
    ...
    ...
}
int main(){
	int s;
	cin>>s;
	for(s;;s++){
		if(f(f(s))==s&&s!=f(s)){
			cout<<s<<" "<<f(s);
			break; 
		}
	}
    return 0;
}

两个数的约数之和,互相为对方

s为输入的数,从这个数开始筛选,寻找A跟B两个非常好友

即B=f(A),A=f(B),所以A=f(f(A))

注意:AB是两个不同的数,所以A不等于B,即s!=f(s)

补充

1. 约数的定义

  • 定义:如果一个整数 a 能够被另一个整数 b 整除,即 amodb=0,那么 b 是 a 的约数(因数)。

2. 常用处理约数的手段

2.1 枚举法

  • 定义:通过遍历所有可能的数来找到一个数的所有约数。

  • 适用场景:适用于较小的数,因为时间复杂度较高。

  • 时间复杂度:O(\sqrt{n}​),其中 n 是目标数。

示例代码

#include <iostream>
#include <vector>
using namespace std;

vector<int> getDivisors(int n) {
    vector<int> divisors;
    for (int i = 1; i * i <= n; ++i) {
        if (n % i == 0) {
            divisors.push_back(i);
            if (i != n / i) { // 避免重复添加平方根
                divisors.push_back(n / i);
            }
        }
    }
    return divisors;
}

int main() {
    int n;
    cout << "请输入一个整数:";
    cin >> n;
    vector<int> divisors = getDivisors(n);
    cout << n << " 的约数有:";
    for (int d : divisors) {
        cout << d << " ";
    }
    cout << endl;
    return 0;
}

2.2 筛法

  • 定义:使用筛法预先计算出一定范围内的所有约数。

  • 适用场景:适用于需要多次查询多个数的约数的情况。

  • 时间复杂度:O(nloglogn),其中 n 是目标范围。

示例代码

#include <iostream>
#include <vector>
using namespace std;

const int MAXN = 100005;
vector<int> divisors[MAXN];

void sieve(int n) {
    for (int i = 1; i <= n; ++i) {
        for (int j = i; j <= n; j += i) {
            divisors[j].push_back(i);
        }
    }
}

int main() {
    int n;
    cout << "请输入一个整数:";
    cin >> n;
    sieve(n);
    cout << n << " 的约数有:";
    for (int d : divisors[n]) {
        cout << d << " ";
    }
    cout << endl;
    return 0;
}

2.3 质因数分解

  • 定义:将一个数分解为质因数的乘积。

  • 适用场景:适用于需要处理较大数的约数问题。

  • 时间复杂度:O(\sqrt{n}​),其中 n 是目标数。

示例代码

#include <iostream>
#include <vector>
using namespace std;

vector<pair<int, int>> primeFactorization(int n) {
    vector<pair<int, int>> factors;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            int count = 0;
            while (n % i == 0) {
                n /= i;
                ++count;
            }
            factors.push_back({i, count});
        }
    }
    if (n > 1) {
        factors.push_back({n, 1});
    }
    return factors;
}

int main() {
    int n;
    cout << "请输入一个整数:";
    cin >> n;
    vector<pair<int, int>> factors = primeFactorization(n);
    cout << n << " 的质因数分解为:";
    for (auto& factor : factors) {
        cout << factor.first << "^" << factor.second << " ";
    }
    cout << endl;
    return 0;
}

2.4 欧几里得算法

  • 定义:用于计算两个数的最大公约数(GCD)。

  • 适用场景:适用于需要处理多个数的公约数问题。

  • 时间复杂度:O(logmin(a,b)),其中 a 和 b 是目标数。

示例代码

#include <iostream>
using namespace std;

int gcd(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

int main() {
    int a, b;
    cout << "请输入两个整数:";
    cin >> a >> b;
    cout << "最大公约数是:" << gcd(a, b) << endl;
    return 0;
}

2.5 约数个数和约数和

  • 定义:通过质因数分解计算约数个数和约数和。

  • 适用场景:适用于需要计算约数个数和约数和的问题。

  • 时间复杂度:O(\sqrt{n}​),其中 n 是目标数。

示例代码

#include <iostream>
#include <vector>
using namespace std;

vector<pair<int, int>> primeFactorization(int n) {
    vector<pair<int, int>> factors;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            int count = 0;
            while (n % i == 0) {
                n /= i;
                ++count;
            }
            factors.push_back({i, count});
        }
    }
    if (n > 1) {
        factors.push_back({n, 1});
    }
    return factors;
}

int main() {
    int n;
    cout << "请输入一个整数:";
    cin >> n;
    vector<pair<int, int>> factors = primeFactorization(n);

    // 计算约数个数
    int numDivisors = 1;
    for (auto& factor : factors) {
        numDivisors *= (factor.second + 1);
    }

    // 计算约数和
    long long sumDivisors = 1;
    for (auto& factor : factors) {
        long long sum = 1;
        long long p = 1;
        for (int i = 0; i < factor.second; ++i) {
            p *= factor.first;
            sum += p;
        }
        sumDivisors *= sum;
    }

    cout << n << " 的约数个数是:" << numDivisors << endl;
    cout << n << " 的约数和是:" << sumDivisors << endl;
    return 0;
}

3. 总结

  • 枚举法:适用于较小的数,时间复杂度为 O(\sqrt{n})。

  • 筛法:适用于需要多次查询多个数的约数的情况,时间复杂度为 O(nloglogn)。

  • 质因数分解:适用于需要处理较大数的约数问题,时间复杂度为 O(\sqrt{n}​)。

  • 欧几里得算法:用于计算两个数的最大公约数,时间复杂度为 O(logmin(a,b))。

  • 约数个数和约数和:通过质因数分解计算约数个数和约数和,时间复杂度为 O(\sqrt{n}​)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值