csp信奥赛C++之约数研究

csp信奥赛C++之约数研究

原题说明:洛谷P1403 [AHOI2005] 约数研究

题目描述

科学家们在 Samuel 星球上的探险得到了丰富的能源储备,这使得空间站中大型计算机 Samuel II 的长时间运算成为了可能。由于在去年一年的辛苦工作取得了不错的成绩,小联被允许用 Samuel II 进行数学研究。

小联最近在研究和约数有关的问题,他统计每个正数 N N N 的约数的个数,并以 f ( N ) f(N) f(N) 来表示。例如 12 12 12 的约数有 1 , 2 , 3 , 4 , 6 , 12 1,2,3,4,6,12 1,2,3,4,6,12,因此 f ( 12 ) = 6 f(12)=6 f(12)=6。下表给出了一些 f ( N ) f(N) f(N) 的取值:

N N N 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6
f ( N ) f(N) f(N) 1 1 1 2 2 2 2 2 2 3 3 3 2 2 2 4 4 4

现在请你求出:

∑ i = 1 n f ( i ) \sum_{i=1}^n f(i) i=1∑nf(i)

输入格式

输入一个整数 n n n。

输出格式

输出答案。

输入输出样例 1
输入 1
复制代码
3
输出 1
复制代码
5
说明/提示
  • 对于 20 % 20\% 20% 的数据, N ≤ 5000 N \leq 5000 N≤5000;
  • 对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 10 6 1 \leq N \leq 10^6 1≤N≤106。

暴力代码

cpp 复制代码
#include <bits/stdc++.h>   
using namespace std;

int n, sum = 0;            // n 为输入上限,sum 累计所有 f(i) 的和

// 计算 x 的约数个数
int f(int x) {
    int cnt = 0;           // 约数计数器
    // 只需枚举到 sqrt(x) 即可成对统计
    for (int i = 1; i <= sqrt(x); i++) {
        if (x % i == 0) {  // i 是 x 的约数
            cnt += 2;       // 将 i 和 x/i 这一对都计入
        }
        if (i * i == x) {   // 若 i 恰好等于 x/i,说明是平方数,之前多算了一次
            cnt--;          // 减去重复的一次
        }
    }
    return cnt;
}

int main() {
    cin >> n;               // 输入 n
    // 枚举 1 到 n 的每个数,累加它们的约数个数
    for (int i = 1; i <= n; i++) {
        sum += f(i);
    }
    cout << sum;            // 输出结果
    return 0;
}
暴力代码TLE 原因分析
  • 数据范围 :题目中 (n) 最大可达 10 6 10^6 106。
  • 外层循环 :共执行 n = 10 6 n = 10^6 n=106 次。
  • 内层函数 f(i) :对于每个 i,需要循环 i \sqrt{i} i 次(取整)。
    总计算量约为: ∑ i = 1 n i ≈ ∫ 0 n x   d x = 2 3 n 3 / 2 \sum_{i=1}^{n} \sqrt{i} \approx \int_{0}^{n} \sqrt{x} \, dx = \frac{2}{3} n^{3/2} ∑i=1ni ≈∫0nx dx=32n3/2
    代入 (n = 10^6):
    2 3 × ( 10 6 ) 3 / 2 = 2 3 × 10 9 ≈ 6.67 × 10 8 \frac{2}{3} \times (10^6)^{3/2} = \frac{2}{3} \times 10^9 \approx 6.67 \times 10^8 32×(106)3/2=32×109≈6.67×108
    即大约 6.7 亿次循环迭代。
  • 结论 :暴力枚举每个数的约数个数的方法在 n = 10 6 n=10^6 n=106 时无法通过时限,必须采用更高效的数学方法(如统计每个约数出现的次数)。

优化思路分析

更高效的方法 :转换角度,统计每个正整数 d作为约数出现在多少个 i 中。

对于给定的 d,在 1 ∼ n 1 \sim n 1∼n 中,d 的倍数有 ⌊ n d ⌋ \left\lfloor \frac{n}{d} \right\rfloor ⌊dn⌋ 个,因此 d对总和的贡献就是 ⌊ n d ⌋ \left\lfloor \frac{n}{d} \right\rfloor ⌊dn⌋。

于是: ∑ i = 1 n f ( i ) = ∑ d = 1 n ⌊ n d ⌋ \sum_{i=1}^n f(i) = \sum_{d=1}^n \left\lfloor \frac{n}{d} \right\rfloor ∑i=1nf(i)=∑d=1n⌊dn⌋

直接循环 d = 1 到 n 累加即可,时间复杂度 O(n),空间复杂度 O(1)。

对于 n ≤ 10 6 n \le 10^6 n≤106,完全可行。

AC代码

cpp 复制代码
#include <bits/stdc++.h>   // 万能头文件
using namespace std;

int main() {
    int n;                 // 输入的上限
    scanf("%d", &n);       // 读入 n

    long long ans = 0;     // 答案可能超出 int 范围,使用 long long

    // 枚举每个可能的约数 d,统计它出现的次数并累加
    for (int d = 1; d <= n; ++d) {
        ans += n / d;      // d 作为约数出现的次数 = floor(n / d)
    }

    printf("%lld\n", ans); // 输出结果
    return 0;
}

【文末福利:一等奖秘籍汇总】(完整csp信奥赛C++学习资料):

1、csp/信奥赛C++,完整信奥赛系列课程(永久学习):

https://edu.csdn.net/lecturer/7901 点击跳转

2、CSP信奥赛C++竞赛拿奖视频课:

https://edu.csdn.net/course/detail/40437 点击跳转

3、csp信奥赛高频考点知识详解及案例实践:

CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html 点击跳转

4、csp信奥赛冲刺一等奖有效刷题题解:

CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新): https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13125089.html 点击跳转

5、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html 点击跳转

· 文末祝福 ·

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"跟着王老师一起学习信奥赛C++";
	cout<<"    成就更好的自己!       ";
	cout<<"  csp信奥赛一等奖属于你!   ";
	return 0;
}
相关推荐
tobias.b1 小时前
408真题解析-2010-41-数据结构-散列表
数据结构·散列表·计算机考研·408真题解析
Eternity∞1 小时前
数据结构基础
c语言·开发语言·数据结构·学习·vim
Yzzz-F1 小时前
牛客寒假算法训练营3
算法
今儿敲了吗1 小时前
32| 伐木
数据结构·笔记·学习·算法
网小鱼的学习笔记1 小时前
leetcode24: 两两交换链表中的节点
数据结构·链表
样例过了就是过了1 小时前
LeetCode热题100 环形链表
算法·leetcode·链表
小老鼠不吃猫2 小时前
Qt C++稳定职业规划
开发语言·c++·qt
小米4962 小时前
day5:92. 反转链表 II
数据结构·链表
努力学算法的蒟蒻2 小时前
day95(2.24)——leetcode面试经典150
算法·leetcode·面试