【二次爆炸记·第二弹】(欧拉筛法求约数和)Savrsen


题目

题目描述

如果一个数等于它的所有因数(小于自身的)之和,那么这个数就是完美的。例如,28是完美的,因为 28 = 1 + 2 + 4 + 7 + 14 28=1+2+4+7+14 28=1+2+4+7+14

基于这个定义,我们数字n的不完美度定为 f ( n ) f(n) fn,它等于n与n的所有因数(小于n的)之和的差的绝对值,因此完美数的不完全度为 0 0 0,其余自然数的不完全度都大于 0 0 0

例如:

f ( 6 ) = ∣ 6 − ( 1 + 2 + 3 ) ∣ = 0 f(6)=|6-(1+2+3)|=0 f6=61+2+3=0

f ( 11 ) = ∣ 11 − ( 1 ) ∣ = 10 f(11)=|11-(1)|=10 f11=111=10

f ( 24 ) = ∣ 24 − ( 1 + 2 + 3 + 4 + 6 + 8 + 12 ) ∣ = ∣ − 12 ∣ = 12 f(24)=|24-(1+2+3+4+6+8+12)|=|-12|=12 f24=241+2+3+4+6+8+12=12=12

写一个程序,对于正整数A和B,计算A和B之间所有数字的不完美度之和:即 f ( A ) + f ( A + 1 ) + … + f ( B ) f(A)+f(A+1)+…+f(B) fA+fA+1++fB

输入格式

第一行输入包含两个整数 A A A B B B 1 ≤ A ≤ B ≤ 1 e 7 1≤A≤B≤1e7 1AB1e7)。表示题目中的 A A A B B B

输出格式

输出一个整数。表示 f ( A ) + f ( A + 1 ) + … + f ( B ) f(A)+f(A+1)+…+f(B) fA+fA+1++fB

样例

样例输入1

1 9

样例输出1

21

样例输入2

24 24

样例输出2

12

解析

第一眼看上去,既然是数轮题,那么首先想到的就应该是 背到的模板 欧拉。

为什么这么说,因为欧拉在数论领域中真心无处不在,图论也有他的一席之地。。。

因此这道题我们手推一下即可发现,这应该是欧拉筛求约数和 的板了吧。。。

欧拉筛法

筛法就是能够快速筛出素数的一种方法,而欧拉筛法则是埃氏筛法的改良版。

因为埃氏筛法并不是线性的筛素数,有些合数可能会被不同的素数筛出去多遍,还可以更加高效,所以欧拉筛法“取长补短”,复杂度也从 O ( n l o g l o g n ) O(n loglogn) O(nloglogn)变成 O ( n ) O(n) O(n)。。。

当然,它的核心思想就是让所有的合数被他的最小质因子给筛出去,以后就可以不用再被筛一次了。而他的线性筛法,也正是因为这样才得以进行的。

贴板真好用

int prim[N + 5], cnt;
bool vis[N + 5];

void prime (int n){
	for (int i = 2; i <= n; i++){
		if (!vis[i]) prim[++cnt] = i;
		for (int j = 1; j <= cnt && prim[j] * i <= n; j++){
			vis[i * prim[j]] = 1;
			if (i % prim[j] == 0) break;
		}
	}
}

注意看上面的j循环,不知道有没有童鞋疑惑为什么不管是不是质数都要枚举这层循环,但是仔细看变量,j实质上是在充当下标使用,让每个i都能乘上所有的prim,把利用素数筛合数的思路放大到最大化。当然这并不代表我不懂哈 强力辩解ing

还有一点困扰了我很久,为什么当 i % p r i m [ j ] = = 0 i \% prim[j] == 0 i%prim[j]==0时就退出?请大家思考几分钟哈(注意结合欧筛的核心思想)~~~



OK,欧筛的核心思想是让合数被他的最小质因子给筛出去,因此,当 i % p r i m [ j ] = = 0 i \% prim[j] == 0 i%prim[j]==0时, i = p r i m [ j ] ∗ x i = prim[j] * x i=prim[j]x x x x是任意正整数。到后面prim数组又添加了新元素后,prim数组中的元素始终是严格递增的,设到后面新添了一个素数 p r i m [ k ] prim[k] prim[k],就要筛去 i ∗ p r i m [ k ] i * prim[k] iprim[k],可是 i ∗ p r i m [ k ] = x ∗ p r i m [ j ] ∗ p r i m [ k ] i * prim[k] = x * prim[j] * prim[k] iprim[k]=xprim[j]prim[k],也就是说这个乘积的最小质因子是 p r i m [ j ] prim[j] prim[j],但在这里却又重新筛了一遍,这不符合欧拉筛的线性特征,因此的证。

筛约数和

首先我们得先了解一下约数和的概念,一个正整数n的约数和,我们记为 s ( n ) s(n) s(n),那么 s ( n ) = ( 1 + p 1 1 + p 1 2 + … + p 1 w 1 ) ( 1 + p 2 1 + p 2 2 + … + p 2 w 2 ) ( 1 + p 3 1 + p 3 2 + … + p 3 w 3 ) … ( 1 + p m 1 + p m 2 + … + p m w m ) s(n) = (1 + p_1^1 + p_1^2 + …+ p_1^{w_1})(1 + p_2^1 + p_2^2 + … + p_2^{w_2})(1 + p_3^1 + p_3^2 + … +p_3^{w_3})…(1 + p_m^1 + p_m^2 + … + p_m^{w_m}) s(n)=(1+p11+p12++p1w1)(1+p21+p22++p2w2)(1+p31+p32++p3w3)(1+pm1+pm2++pmwm)。其中 p i p_i pi n n n的质因子,而 w i w_i wi则是 p i p_i pi的次数。

有了这个东西,我们理解欧拉筛法筛约数和就方便多了。

假设现在考虑到 i i i了,那么就按照上面欧拉筛法的两种情况来讨论一下,假设 i i i是素数,那么 s [ i ] s[i] s[i]肯定就等于 i + 1 i + 1 i+1

假设 i % p r i m [ j ] ! = 0 i \% prim[j] != 0 i%prim[j]!=0,就说明 i i i不包含 p r i m [ j ] prim[j] prim[j]这个质因子,但是 s [ i ] s[i] s[i]的所有值肯定能对 s [ i ∗ p r i m [ j ] ] s[i * prim[j]] s[iprim[j]]做上贡献,意思就是说i得质因子肯定是 i ∗ p r i m [ j ] i * prim[j] iprim[j]的质因子,所以 s [ i ∗ p r i m [ j ] ] s[i * prim[j]] s[iprim[j]]中肯定包含了 s [ i ] s[i] s[i],再来看,因为 p r i m [ j ] prim[j] prim[j]是素数,他的因子只包含 1 1 1和它本身,因此 i ∗ p r i m [ j ] i * prim[j] iprim[j]的因子肯定也只比i多了他的因子乘上 p r i m [ j ] prim[j] prim[j]

不好懂??这个理解方法确实有点抽象,那么我再来举一个具体的例子:

因为 s ( i ) = ( 1 + p 1 1 + p 1 2 + … + p 1 w 1 ) ( 1 + p 2 1 + p 2 2 + … + p 2 w 2 ) ( 1 + p 3 1 + p 3 2 + … + p 3 w 3 ) … ( 1 + p m 1 + p m 2 + … + p m w m ) s(i) = (1 + p_1^1 + p_1^2 + …+ p_1^{w_1})(1 + p_2^1 + p_2^2 + … + p_2^{w_2})(1 + p_3^1 + p_3^2 + … +p_3^{w_3})…(1 + p_m^1 + p_m^2 + … + p_m^{w_m}) s(i)=(1+p11+p12++p1w1)(1+p21+p22++p2w2)(1+p31+p32++p3w3)(1+pm1+pm2++pmwm),且 i i i不包含 p r i m [ j ] prim[j] prim[j]这个最小质因子,那么我们假设 p r i m [ j ] prim[j] prim[j] p m + 1 p_{m+1} pm+1,它的次数为 1 1 1
因此 s ( i ∗ p r i m [ j ] ) = ( 1 + p 1 1 + p 1 2 + … + p 1 w 1 ) ( 1 + p 2 1 + p 2 2 + … + p 2 w 2 ) ( 1 + p 3 1 + p 3 2 + … + p 3 w 3 ) … … ( 1 + p m 1 + p m 2 + … + p m w m ) ( 1 + p m + 1 1 ) = s ( i ) ∗ ( 1 + p m + 1 ) s(i * prim[j]) = (1 + p_1^1 + p_1^2 + …+ p_1^{w_1})(1 + p_2^1 + p_2^2 + … + p_2^{w_2})(1 + p_3^1 + p_3^2 + … +p_3^{w_3})……(1 + p_m^1 + p_m^2 + … + p_m^{w_m})(1 + p_{m + 1}^1) = s(i) * (1 + p_{m + 1}) s(iprim[j])=(1+p11+p12++p1w1)(1+p21+p22++p2w2)(1+p31+p32++p3w3)(1+pm1+pm2++pmwm)(1+pm+11)=s(i)(1+pm+1)

假设 i % p r i m [ j ] = = 0 i \% prim[j] == 0 i%prim[j]==0,就说明 i = p r i m [ j ] ∗ x i = prim[j] * x i=prim[j]x x x x同上),而在这里, p r i m [ j ] prim[j] prim[j]就相当于 p 1 p_1 p1
因此 s ( i ∗ p r i m [ j ] ) = ( 1 + p 1 1 + p 1 2 + … + p 1 w 1 + p 1 w 1 + 1 ) ( 1 + p 2 1 + p 2 2 + … + p 2 w 2 ) ( 1 + p 3 1 + p 3 2 + … + p 3 w 3 ) … … ( 1 + p m 1 + p m 2 + … + p m w m ) s(i * prim[j]) = (1 + p_1^1 + p_1^2 + …+ p_1^{w_1} + p_1^{w_1 + 1})(1 + p_2^1 + p_2^2 + … + p_2^{w_2})(1 + p_3^1 + p_3^2 + … +p_3^{w_3})……(1 + p_m^1 + p_m^2 + … + p_m^{w_m}) s(iprim[j])=(1+p11+p12++p1w1+p1w1+1)(1+p21+p22++p2w2)(1+p31+p32++p3w3)(1+pm1+pm2++pmwm)

可是这里一大串等比数列求和太冗杂了,显然一个一个算出来肯定不现实,而我们又想到
s ( i ) = ( 1 + p 1 1 + p 1 2 + … + p 1 w 1 ) ( 1 + p 2 1 + p 2 2 + … + p 2 w 2 ) ( 1 + p 3 1 + p 3 2 + … + p 3 w 3 ) … ( 1 + p m 1 + p m 2 + … + p m w m ) s(i) = (1 + p_1^1 + p_1^2 + …+ p_1^{w_1})(1 + p_2^1 + p_2^2 + … + p_2^{w_2})(1 + p_3^1 + p_3^2 + … +p_3^{w_3})…(1 + p_m^1 + p_m^2 + … + p_m^{w_m}) s(i)=(1+p11+p12++p1w1)(1+p21+p22++p2w2)(1+p31+p32++p3w3)(1+pm1+pm2++pmwm)

在这里 s ( i ) s(i) s(i) s ( i ∗ p r i m [ j ] ) s(i * prim[j]) s(iprim[j])相差甚小,我们能用一些什么方法将 s ( i ) s(i) s(i)变成 s ( i ∗ p r i m [ j ] ) s(i * prim[j]) s(iprim[j])吗?

答案是肯定的。我们可以记一个数组 p s u m psum psum表示关于 i i i的最小质因子的等比数列求和,那么我们就可以将 s ( i ) s(i) s(i)除掉 p s u m [ i ] psum[i] psum[i],也就是最前面的那一个等比数列,再乘上一个新的等比数列 p s u m [ i ∗ p r i m [ j ] ] psum[i * prim[j]] psum[iprim[j]]就行了。

当然, p s u m psum psum显然也是可维护的,因为 p s u m [ i ∗ p r i m [ j ] ] psum[i * prim[j]] psum[iprim[j]]用眼看可发现就等于 p s u m [ i ] ∗ p r i m [ j ] + 1 psum[i] * prim[j] + 1 psum[i]prim[j]+1

至此, s s s的所有情况都已经讨论完毕了。

当然,这道题和欧拉筛法约数和有一个小地方不同,也就是 s [ i ] s[i] s[i]不能包括i本身。不过这个应该是个 O I e r OIer OIer都会解决吧、、、

Code

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <string>
#include <queue>
#include <stack>
#include <cstring>
#include <iostream>
using namespace std;
#define reg register
#define LL long long
#define INF 0x3f3f3f3f

template<typename T>
void re (T &x){
    x = 0;
    int f = 1;
    char c = getchar ();
    while (c < '0' || c > '9'){
        if (c == '-') f = -1;
        c = getchar ();
    }
    while (c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + c - 48;
        c = getchar ();
    }
    x *= f;
}

template<typename T>
void pr (T x){
    if (x < 0){
        putchar ('-');
        x = ~x + 1;
    }
    if (x / 10) pr (x / 10);
    putchar (x % 10 + 48);
}

const int N = 1e7, Maxn = 670000;

int A, B, prim[Maxn + 5], cnt, sum[N + 5], psum[N + 5];
LL ans;
bool vis[N + 5];

LL fabs (LL x){
    return (x > 0) ? x : ((~x) + 1);
}

void sieve (int n){
	sum[1] = 1;
    for (int i = 2; i <= n; i++){
        if (!vis[i]){
            prim[++cnt] = i;
            psum[i] = sum[i] = i + 1;
        }
        for (int j = 1; j <= cnt && i * prim[j] <= n; j++){
            vis[i * prim[j]] = 1;
            if (i % prim[j] == 0){
                psum[i * prim[j]] = psum[i] * prim[j] + 1;
                sum[i * prim[j]] = sum[i] / psum[i] * psum[i * prim[j]];
				break;
            }
            psum[i * prim[j]] = prim[j] + 1;
            sum[i * prim[j]] = sum[i] * (prim[j] + 1);
        }
    }
}

int main (){
    //freopen ("savrsen.in", "r", stdin);
    //freopen ("savrsen.out", "w", stdout);
    re (A); re (B);
    sieve (B);
    for (int i = A; i <= B; i++){
        sum[i] -= i;
        ans = ans + fabs (i * 1ll - sum[i] * 1ll);
    }
    pr (ans);
    putchar (10);
    return 0;
}

后记

其实欧拉筛法能筛掉很多东西的,但是因为这道题只和约数和有关,因此我就略掉其他不提,只讲约数和。有兴趣的童鞋可以去康康这篇同机房ju’lao博客

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值