P1403 [AHOI2005]约数研究

约数个数和

这里还是用到辣个公式:

\[\sum_{i=1}^nd_1(i)=\sum_{i=1}^n\lfloor \frac{n}{i} \rfloor\]

注意:这个公式对总体成立,对个体不成立!

所以你就有两种思路:

  1. 转化为“余数求和”那种思路,使用除法分块来解决这打道题。

  2. 直接上线性筛(我的思路)。

线性筛怎么求出约数个数?

约数个数定理

一个数\(x\)可以分解质因数变成这样的样子:

\[x=a_1^{p_1}a_2^{p_2}...a_n^{p_n}\]

那么它的约数个数可以表示为:

\[(1+p_1)(1+p_2)...(1+p_n)\]

那么我们就可以使用约数个数定理结合线性筛来求约数个数了。

前提:我们使用一个数组记录最小的\(p\)\(p_1\),这里用mm表示。

当一个数是质数

显然约数个数只有2个,且最小的项是1,因为只能分解出1和它自己。

\(i \bmod prime[j]\)不为0时

这个时候说明一个很重要的事情:\(prime[j]\)是我们要筛那个数的最小素因子。这个结论来自线性筛的性质。

所以被筛的那个数的约数个数式子的第一项一定会变成\((1+1)\),所以那个\(p_1\)更新为1。

那么新数的约数个数就需要乘以2了。为了方便并且体现出积性函数的性质,你可以像我那样那么写。

\(i \bmod prime[j]\)为0时

这个时候说明新数中其实就有\(prime[j]\)这个因子了,而上面的情况是没有的。

所以新数就是在原来的最小素因子的指数上再加了个1。因为我现在多乘了它,所以指数需要加1。

那么新数的第一项就会变成\((1+p_1+1)\)。所以约数个数,我们要先除以\((1+p_1)\)再乘以\((2+p_1)\)

这就是为什么我们要记录最小素因子的原因。需要在这里用到。

然后就直接实现就好了哇。

代码:

#include<cstdio>

const int maxn = 1000005;
bool notprime[maxn];
int prime[maxn], tot;
int ys[maxn], mm[maxn];
int n;
void init()
{
    notprime[1] = true;
    ys[1] = 1;
    for(int i = 2; i <= n; i++)
    {
        if(!notprime[i])
        {
            prime[++tot] = i;
            ys[i] = 2;
            mm[i] = 1;
        }
        for(int j = 1; j <= tot && i * prime[j] <= n; j++)
        {
            notprime[i * prime[j]] = true;
            if(i % prime[j] != 0)
            {
                ys[i * prime[j]] = ys[i] * ys[prime[j]];
                mm[i * prime[j]] = 1;
            }
            else
            {
                ys[i * prime[j]] = ys[i] / (mm[i] + 1) * (mm[i] + 1 + 1);
                mm[i * prime[j]] = mm[i] + 1;
                break;
            }
        }
    }
}
int main()
{
    scanf("%d", &n);
    init();
    long long ans = 0;
    for(int i = 1; i <= n; i++) ans += ys[i];
    printf("%lld\n", ans);
    return 0;
}

PS

线性筛这些东西稍不注意就打错了,而暴力分解质因数就没那么容易错了。所以你可以拿去对拍哦!

转载于:https://www.cnblogs.com/Garen-Wang/p/9882504.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值