针对CSP-J/S的每日一练:Day 5

博客围绕珂朵莉数列问题展开,先给出题目描述、输入输出格式及样例,题目要求计算数列 {bn} 前 N 项。接着介绍思路,利用数列性质,通过约数分块算法统计因子个数,时间复杂度为 O(n√n),最后给出示例代码。

一、审题

题目描述

珂朵莉有一个正整数数列 {an}\{a_n\}{an},对于所有的 i≥1i\geq 1i1ai+1=ai×i+1a_{i+1}=a_{i}\times i+1ai+1=ai×i+1
现在她定义了一个新的数列 {bn}\{b_n\}{bn},对于所有的 i≥1i\geq 1i1bib_ibi 表示有多少个 aja_jaj,满足 1≤j<i1\leq j<i1j<iaja_jajaia_iai 的因子。
请你帮助珂朵莉计算出数列 {bn}\{b_n\}{bn} 的前 NNN 项。

输入格式

输入文件的第一行包含一个整数 NNN

输出格式

输出文件共 NNN 行,第 iii 行表示 bib_ibi 的值。

输入样例

10

输出样例

0
1
1
1
2
1
3
1
2
4

题目来源

2018 CSP-J No.3

二、思路

珂朵莉的数列 a1⋯na_{1\cdots n}a1n 有一个特点:ai mod aj>0(i>j)a_i \bmod a_j>0(i>j)aimodaj>0(i>j)。因为如果存在i>ji>ji>j,满足aj∣aia_j|a_iajai,那么ai+1 mod aj=ai×i+1 mod aj=1a_{i+1}\bmod a_j=a_i\times i+1 \bmod a_j=1ai+1modaj=ai×i+1modaj=1,那么aj∣ai+1a_j|a_{i+1}ajai+1,所以不满足原式。在这个性质的基础上,我们可以通过约数分块算法来统计每个数的因子个数,时间复杂度为 O(nn)O(n\sqrt n)O(nn)

约数分块算法的基本思路是,将每个数 xxx 的因子分为两类:小于等于 x\sqrt xx 的因子和大于 x\sqrt xx 的因子。只需要先统计小于等于 x\sqrt xx 的因子数,然后再计算大于 x\sqrt xx 的因子数。这样,我们只需要枚举 n\sqrt nn 个因子即可完成统计。具体实现时,可以先预处理每个数的小于等于 n\sqrt nn 的因子个数,然后针对每个 iii,枚举其大于 i\sqrt ii 的因子,根据约数的对称性,即可计算出小于 i\sqrt ii 的因子个数,从而求出 bib_ibi

时间复杂度分析:对于每个 xxx,通过约数分块算法只需要枚举 x\sqrt xx 个因子,因此时间复杂度为 O(nn)O(n\sqrt n)O(nn)

三、示例代码

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

const int MAXN = 1005;
int b[MAXN];
vector<int> factors[MAXN];  // 存储每个数的因子

int main()
{
    int n;
    cin >> n;

    // 预处理每个数的因子
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j * j <= i; j++)
        {
            if (i % j == 0)
            {
                factors[i].push_back(j);
                if (j != i / j)
                {
                    factors[i].push_back(i / j);
                }
            }
        }
    }

    for (int i = 1; i <= n; i++)
    {
        // 先计算小于等于sqrt(i)的因子个数
        for (int j = 0; j < factors[i].size(); j++)
        {
            if (factors[i][j] <= sqrt(i))
            {
                b[i]++;
            }
        }

        // 再计算大于sqrt(i)的因子个数
        for (int j = 0; j < factors[i].size(); j++)
        {
            if (factors[i][j] > sqrt(i))
            {
                if (factors[i][j] < i)
                {
                    b[i]++;
                }
            }
        }

        cout << b[i] << endl;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值