线性筛d&&sd(约数个数&&约数和)

d d 函数

d(n)表示n的约数个数和

百度一下,我们就可以得到一个计算d函数的暴力做法

n=pa11pa22...pakk n = p 1 a 1 p 2 a 2 . . . p k a k
d(n)=(a1+1)(a2+1)...(ak+1) d ( n ) = ( a 1 + 1 ) ( a 2 + 1 ) . . . ( a k + 1 )

显然d是一个积性函数,我们可以用线性筛求出

prime[i] p r i m e [ i ] 表示第 i i 个质数
num[i]表示 i i 的最小质因子出现次数

首先我们可以观察一下线性筛素数:

void prepare() {
    for (int i=2;i<N;i++) {
        if (!no[i]) sshu[++tot]=i;
        for (int j=1;j<=tot&&sshu[j]*i<N;j++) {
            no[sshu[j]*i]=1;
            if (i%sshu[j]==0) {
                no[sshu[j]*i]=1;
                break;
            }
        }
    }
}

我们之所以可以保证上述算法是线性的
就是因为:if (i%sshu[j]==0) break;
也就是说,每个数只会被ta的最小质因子筛掉

① 当前数是质数

很显然,d[n]=2,num[n]=1

②i%prime[j]!=0

这种情况下, i i 没有prime[j]这个质因子
然而 iprime[j] i ∗ p r i m e [ j ] 中包含了一个 prime[j] p r i m e [ j ]
前面我们已经得到了 d[i] d [ i ]
d[i]=(a1+1)(a2+1)...(ak+1) d [ i ] = ( a 1 + 1 ) ( a 2 + 1 ) . . . ( a k + 1 )

然后我们在这之后补上一个 prime[j] p r i m e [ j ]
d[iprime[j]]=(a1+1)(a2+1)...(ak+1)(1+1) d [ i ∗ p r i m e [ j ] ] = ( a 1 + 1 ) ( a 2 + 1 ) . . . ( a k + 1 ) ( 1 + 1 )
d[iprime[j]]=d[i]d[prime[j]]=d[i]2 d [ i ∗ p r i m e [ j ] ] = d [ i ] ∗ d [ p r i m e [ j ] ] = d [ i ] ∗ 2

而且由于当前的 prime[j] p r i m e [ j ] 必然是 iprime[j] i ∗ p r i m e [ j ] 的最小素因子(因为从小到大枚举), 我们要记录下这个最小素因子的个数

num[iprime[j]]=1 n u m [ i ∗ p r i m e [ j ] ] = 1

③i%prime[j]=0

这种情况下,i中必然包含了至少一个 prime[j] p r i m e [ j ]
而且 prime[j] p r i m e [ j ] 也必定是 i i 的最小质因数

iprime[j]比起 i i 则是多了一个最小质因子

d[i]=(a1+1)(a2+1)...(ak+1)
d[iprime[j]]=(a1+1+1)(a2+1)...(ak+1) d [ i ∗ p r i m e [ j ] ] = ( a 1 + 1 + 1 ) ( a 2 + 1 ) . . . ( a k + 1 )

那么 d[iprime[j]]=d[i]/(a1+1)(a1+2) d [ i ∗ p r i m e [ j ] ] = d [ i ] / ( a 1 + 1 ) ∗ ( a 1 + 2 )
num[iprime[j]]=num[i]+1 n u m [ i ∗ p r i m e [ j ] ] = n u m [ i ] + 1

void prepare() {
    d[1]=1; num[1]=1;
    for (int i=2;i<N;i++) {
        if (!no[i]) {
            sshu[++tot]=i;
            d[i]=2; num[i]=1;
        }
        for (int j=1;j<=tot&&sshu[j]*i<N;j++) {
            int v=sshu[j]*i;
            no[v]=1;
            if (i%sshu[j]==0) {
                num[v]=num[i]+1;
                d[v]=d[i]/num[v]*(num[v]+1);
                break;
            }
            d[v]=d[i]<<1; num[v]=1;
        }
    }
    //for (int i=1;i<=10;i++) printf("%d\n",d[i]);
}

sd s d 函数

sd(n) s d ( n ) 表示 n n 的所有约数之和

再次百度一下,我们就可以得到一种比较暴力的sd计算方法

n=pa11pa22...pakk n = p 1 a 1 p 2 a 2 . . . p k a k
sd(n)=(1+p1+p21+p31+...+pa11)(1+p2+p22+...+pa22)...(1+p3+p23+...+pa33) s d ( n ) = ( 1 + p 1 + p 1 2 + p 1 3 + . . . + p 1 a 1 ) ( 1 + p 2 + p 2 2 + . . . + p 2 a 2 ) . . . ( 1 + p 3 + p 3 2 + . . . + p 3 a 3 )

又是一个积性函数,我们可以考虑用线性筛筛出sd函数

prime[i] p r i m e [ i ] 表示第 i i 个质数
sp[i]表示 i i (1+p1+p12+...+p1a1)

① 当前数是质数

显然 sd[n]=n+1,sq[n]=n+1 s d [ n ] = n + 1 , s q [ n ] = n + 1

②i%prime[j]!=0

这种情况, i i 中不包含prime[j]这个质因子
而且 prime[j] p r i m e [ j ] 一定是 iprime[j] i ∗ p r i m e [ j ] 的最小质因子

sd[i]=(1+p1+p21+...+pa11)(1+p2+p22+...+pa22)...(1+p3+p23+...+pa33) s d [ i ] = ( 1 + p 1 + p 1 2 + . . . + p 1 a 1 ) ( 1 + p 2 + p 2 2 + . . . + p 2 a 2 ) . . . ( 1 + p 3 + p 3 2 + . . . + p 3 a 3 )
sd[iprime[j]]=(1+p1+p21+...+pa11)...(1+p3+p23+...+pa33)(1+prime[j]) s d [ i ∗ p r i m e [ j ] ] = ( 1 + p 1 + p 1 2 + . . . + p 1 a 1 ) . . . ( 1 + p 3 + p 3 2 + . . . + p 3 a 3 ) ( 1 + p r i m e [ j ] )

sd[iprime[j]]=sd[i]sd[prime[j]] s d [ i ∗ p r i m e [ j ] ] = s d [ i ] ∗ s d [ p r i m e [ j ] ]
sp[iprime[j]]=sp[prime[j]]=prime[j]+1 s p [ i ∗ p r i m e [ j ] ] = s p [ p r i m e [ j ] ] = p r i m e [ j ] + 1

③i%prime[j]=0

这种情况下, i i 中至少包含了一个prime[j]

那么:
sd[i]=(1+p1+p21+...+pa11)(1+p2+p22+...+pa22)...(1+p3+p23+...+pa33) s d [ i ] = ( 1 + p 1 + p 1 2 + . . . + p 1 a 1 ) ( 1 + p 2 + p 2 2 + . . . + p 2 a 2 ) . . . ( 1 + p 3 + p 3 2 + . . . + p 3 a 3 )
sd[iprime[j]]=(1+p1+p21+...+pa11+pa1+11)...(1+p3+p23+...+pa33) s d [ i ∗ p r i m e [ j ] ] = ( 1 + p 1 + p 1 2 + . . . + p 1 a 1 + p 1 a 1 + 1 ) . . . ( 1 + p 3 + p 3 2 + . . . + p 3 a 3 )

我们观察一下两式唯一的不同:
(1+p1+p21+...+pa11) ( 1 + p 1 + p 1 2 + . . . + p 1 a 1 )
(1+p1+p21+...+pa11+pa1+11) ( 1 + p 1 + p 1 2 + . . . + p 1 a 1 + p 1 a 1 + 1 )

sd[iprime[j]]=sd[i]/(1+p1+p21+...+pa11)(1+p1+p21+...+pa11+pa1+11) s d [ i ∗ p r i m e [ j ] ] = s d [ i ] / ( 1 + p 1 + p 1 2 + . . . + p 1 a 1 ) ∗ ( 1 + p 1 + p 1 2 + . . . + p 1 a 1 + p 1 a 1 + 1 )

那么
sd[iprime[j]]=sd[i]/sp[i](sp[i]prime[j]+1) s d [ i ∗ p r i m e [ j ] ] = s d [ i ] / s p [ i ] ∗ ( s p [ i ] ∗ p r i m e [ j ] + 1 )
sp[iprime[j]]=sp[i]prime[j]+1 s p [ i ∗ p r i m e [ j ] ] = s p [ i ] ∗ p r i m e [ j ] + 1

void prepare() {
    sd[1]=1; sp[1]=1;
    for (int i=2;i<N;i+=) {
        if (!no[i]) {
            sshu[++tot]=i;
            sd[i]=i+1;
            sp[i]=i+1;
        }
        for (int j=1;j<=tot&&sshu[j]*i<N;j++) {
            int v=sshu[j]*i;
            no[v]=1;
            if (i%sshu[j]==0) {
                sp[v]=sp[i]*sshu[j]+1;
                sd[v]=sd[i]/sp[i]*sp[v];
                break;
            }
            sd[v]=sd[i]*sd[sshu[j]];
            sp[v]=sshu[j]+1;
        }
    }
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值