杜教筛(上):整除分块,积性函数,欧拉与莫比乌斯

整除分块:

当我们求 ∑ i = 1 n f ( ⌊ n i ⌋ ) \sum_{i=1}^nf(\lfloor\frac{n}{i}\rfloor) i=1nf(in)的时候,如果1到n求一遍感觉太傻了,因为会有很多重复的计算,例如:n=10000时,i在[101,111]时,都有 ⌊ n i ⌋ = 9 \lfloor\frac{n}{i}\rfloor=9 in=9,所以我们只需要对所有数分成如上的一个一个区间就可以节省很多不必要的时间

设: ⌊ n i ⌋ = d \lfloor\frac{n}{i}\rfloor=d in=d,那么这段区间的右端点应该是 ⌊ n d ⌋ \lfloor\dfrac{n}{d}\rfloor dn,左端点应该是 ⌊ n d + 1 ⌋ + 1 \lfloor\dfrac{n}{d+1}\rfloor+1 d+1n+1

int f(int a){...}

int deal(int n){
    int ans=0;
    for(int l=1,r;l<=n;l=r+1){
        r=n/(n/l);//计算出与l相同值的区间的右端点
        ans+=(r-l+1)*f(n/l);
    }
}

时间复杂度从 O ( n ) → O ( n ) O(n)\to O(\sqrt n) O(n)O(n )

当然, [ n i ] [ m i ] 的 情 况 只 需 要 r = n / ( n / l ) → r = m i n ( n / ( n / l ) , m / ( m / l ) ) [\frac{n}{i}][\frac{m}{i}]的情况只需要r=n/(n/l)\to r=min(n/(n/l),m/(m/l)) [in][im]r=n/(n/l)r=min(n/(n/l),m/(m/l))


积性函数:

当函数 f ( a ) ∗ f ( b ) = f ( a ∗ b ) f(a)*f(b)=f(a*b) f(a)f(b)=f(ab)时,我们称之为积性函数

而基本上所有的积性函数都可以用线性筛来 O ( n ) O(n) O(n)预处理,因为每个数只会被其最小的素因子筛掉

性质:

  1. 积性*积性=积性(狄利克雷卷积)
  2. ∑ d ∣ n \sum_{d|n} dn积性=积性

最具代表性的两个积性函数为欧拉函数 ϕ ( d ) \phi(d) ϕ(d)和莫比乌斯函数 u ( d ) u(d) u(d)

这两个知识点已经另写了,这里就不累牍了,有一点要注意的是,两个积性函数的狄利克雷卷积也是积性函数。

下面会用到的有两个函数各自的求和定理:
∑ d ∣ n n = ̸ 1 u ( d ) = 0 , ∑ d ∣ n ϕ ( d ) = n \sum_{d|n}^{n=\not1}u(d)=0,\sum_{d|n}\phi(d)=n dnn≠1u(d)=0,dnϕ(d)=n

联系:
&ThickSpace; \\\;
欧拉函数其实是莫比乌斯函数的演变,因为其定义比较直观所以不由莫比乌斯函数代替,等式如下: ϕ ( n ) = ∑ d ∣ n u ( d ) n d \phi(n)=\sum_{d|n}u(d)\frac{n}{d} ϕ(n)=dnu(d)dn证明:(当然狄利克雷卷积可以直接证)
&ThickSpace; \\\;
这里用到的是莫比乌斯函数的定义,当 d = 1 d=1 d=1时,结果为n,当 d = ̸ 1 d=\not1 d≠1时,就相当于对因子的容斥过程了
根据容斥原理,减去一个质因子组成的因子的情况,加上两个质因子组成的情况……一个质因子时u为-1,两个时为1,刚好就可以写成上述式子。(莫比乌斯函数也正因此叫做容斥因子)


杜教筛:

做了这么多铺垫,终于上正餐了

杜教筛用于求积性函数的前缀和,一般可以达到 O ( n 2 3 ) O(n^{\frac{2}{3}}) O(n32)的复杂度,这里以欧拉和莫比乌斯为例讲解

重点: ∑ i = 1 n ∑ d ∣ i f ( d ) → ∑ i = 1 n ∑ j = 1 ⌊ n i ⌋ f ( j ) \sum_{i=1}^n\sum_{d|i}f(d)\to \sum_{i=1}^n\sum_{j=1}^{\lfloor\frac{n}{i}\rfloor}f(j) i=1ndif(d)i=1nj=1inf(j)

应用:

  1. 用于递归,即出现另一个i=1 to n
  2. 用于分块, n / i n/i n/i可以用于整除分块

证明:

对于任意数p,在前半部分中,出现 f ( p ) f(p) f(p)当且仅当 p ∣ i p|i pi i = p , 2 p . . . ⌊ n p ⌋ i=p,2p...\lfloor\frac{n}{p}\rfloor i=p,2p...pn,即出现 ⌊ n p ⌋ \lfloor\dfrac{n}{p}\rfloor pn

而后半部分当且仅当 p &lt; = ⌊ n i ⌋ p&lt;=\lfloor\dfrac{n}{i}\rfloor p<=in,而当 i &lt; = ⌊ n p ⌋ i&lt;=\lfloor\dfrac{n}{p}\rfloor i<=pn时,此式成立,所以也是 ⌊ n p ⌋ \lfloor\dfrac{n}{p}\rfloor pn次,所以两式相等


莫比乌斯函数求和:

∑ i = 1 n u ( n ) , n ∈ [ 1 , 1 e 11 ] \sum_{i=1}^nu(n),n\in[1,1e11] i=1nu(n),n[1,1e11]

∑ d ∣ n n = ̸ 1 u ( d ) = 0 , ∑ d ∣ n n = 1 u ( d ) = 1 \sum_{d|n}^{n=\not1}u(d)=0,\sum_{d|n}^{n=1}u(d)=1 dnn≠1u(d)=0,dnn=1u(d)=1

→ ∑ i = 1 n ∑ d ∣ i u ( d ) = 1 \to \sum_{i=1}^n\sum_{d|i}u(d)=1 i=1ndiu(d)=1

→ ∑ i = 1 n ∑ j = 1 ⌊ n i ⌋ u ( j ) = 1 \to \sum_{i=1}^n\sum_{j=1}^{\lfloor\frac{n}{i}\rfloor}u(j)=1 i=1nj=1inu(j)=1当i=1时,有 ∑ j = 1 n u ( j ) \sum_{j=1}^{n}u(j) j=1nu(j)也就是要求的答案,所以式子就可以化成: ∑ i = 1 n u ( i ) = 1 − ∑ i = 2 n ∑ j = 1 ⌊ n i ⌋ u ( j ) \sum_{i=1}^{n}u(i)=1- \sum_{i=2}^n\sum_{j=1}^{\lfloor\frac{n}{i}\rfloor}u(j) i=1nu(i)=1i=2nj=1inu(j)枚举后半部分的 i i i,发现右边的部分可以递归,但是当n比较小的时候这样拆的效率不如线性筛,而这个界限为 n 2 3 n^{\frac{2}{3}} n32,也就是说 n &gt; n 0 2 3 n&gt;n_{0}^{\frac{2}{3}} n>n032时递归,否则线性筛出。

当然,你可以预处理出u的 n 2 3 n^{\frac{2}{3}} n32的前缀和,就不要每次做一遍了

再看这个式子, ∑ j = 1 ⌊ n i ⌋ \sum_{j=1}^{\lfloor\frac{n}{i}\rfloor} j=1in,这不就是上面的整除分块吗,所以再次优化,标准的杜教筛就完成了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N=1000001;//按题目上限的三分之二次幂,这里的求和范围默认为1e9

int u[N];
int pre[N];
int pri[N>>1],now;
bool vis[N];
void init(){
	u[1]=1;
    for(int i=2;i<N;i++){
        if(!vis[i]){
            pri[++now]=i;u[i]=-1;
        }
        for(int j=1;j<=now&&i*pri[j]<N;j++){
            vis[i*pri[j]]=1;
            u[i*pri[j]]=-u[i];
            if(i%pri[j]==0){
                u[i*pri[j]]=0;
                break;
            }
        }
    }
    pre[0]=0;
    for(int i=1;i<N;i++)pre[i]=u[i]+pre[i-1];
}

unordered_map<LL,LL>Map;
LL calculate(LL n){
    if(n<N)return pre[n];
    if(Map.count(n))return Map[n];
    LL ans=1;
    for(int l=2,r;l<=n;l=r+1){
        r=n/(n/l);
        ans-=(r-l+1)*calculate(n/l);
    }
    return Map[n]=ans;
}

int main(){
    init();
    LL n;
    while(cin>>n){
        printf("%lld\n",calculate(n));
    }
}

欧拉函数求和:

根据定理, ∑ d ∣ n ϕ ( d ) = n \sum_{d|n}\phi(d)=n dnϕ(d)=n,那么有: ∑ i = 1 n ∑ d ∣ i ϕ ( d ) = n ∗ ( n + 1 ) 2 \sum_{i=1}^n\sum_{d|i}\phi(d)=\dfrac{n*(n+1)}{2} i=1ndiϕ(d)=2n(n+1)还是那一步: ∑ i = 1 n ∑ d ∣ i ϕ ( d ) = ∑ i = 1 n ∑ j = 1 [ n i ] ϕ ( j ) = ∑ i = 2 n ∑ j = 1 [ n i ] ϕ ( j ) + ∑ j = 1 n ϕ ( j ) → ∑ i = 1 n ϕ ( i ) = n ∗ ( n + 1 ) 2 − ∑ i = 2 n ∑ j = 1 [ n i ] ϕ ( j ) \sum_{i=1}^n\sum_{d|i}\phi(d)=\sum_{i=1}^n\sum_{j=1}^{[\frac{n}{i}]}\phi(j)=\sum_{i=2}^n\sum_{j=1}^{[\frac{n}{i}]}\phi(j)+\sum_{j=1}^{n}\phi(j)\\\to\sum_{i=1}^{n}\phi(i)=\dfrac{n*(n+1)}{2}-\sum_{i=2}^n\sum_{j=1}^{[\frac{n}{i}]}\phi(j) i=1ndiϕ(d)=i=1nj=1[in]ϕ(j)=i=2nj=1[in]ϕ(j)+j=1nϕ(j)i=1nϕ(i)=2n(n+1)i=2nj=1[in]ϕ(j)


总结:

杜教筛将大范围用递归变成小范围,再用线性筛处理小范围的答案。

开局要得到 ∑ d ∣ n f ( d ) \sum_{d|n}f(d) dnf(d),然后 ∑ i = 1 n ∑ d ∣ i f ( d ) = ∑ i = 1 n ∑ j = 1 [ n i ] f ( j ) = ∑ i = 2 n ∑ j = 1 [ n i ] f ( j ) + ∑ j = 1 n f ( j ) → ∑ i = 1 n f ( i ) = ∑ i = 1 n ∑ d ∣ i f ( d ) − ∑ i = 2 n ∑ j = 1 [ n i ] f ( j ) \sum_{i=1}^n\sum_{d|i}f(d)=\sum_{i=1}^n\sum_{j=1}^{[\frac{n}{i}]}f(j)=\sum_{i=2}^n\sum_{j=1}^{[\frac{n}{i}]}f(j)+\sum_{j=1}^{n}f(j)\\\to\sum_{i=1}^{n}f(i) =\sum_{i=1}^n\sum_{d|i}f(d)-\sum_{i=2}^n\sum_{j=1}^{[\frac{n}{i}]}f(j) i=1ndif(d)=i=1nj=1[in]f(j)=i=2nj=1[in]f(j)+j=1nf(j)i=1nf(i)=i=1ndif(d)i=2nj=1[in]f(j)最后,递归+整除分块+线性筛,完成收尾。


这里只是一部分的杜教筛,因为篇幅过长,分为上下两篇,下一部分的杜教筛需要用到狄利克雷卷积,而上半部分中默认狄利克雷卷积中的g为1,当然这部分的杜教筛已经可以解决一部分问题了,有兴趣的可以学一下下一部分。

下一部分链接

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值