杜教筛

Posted poweryao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了杜教筛相关的知识,希望对你有一定的参考价值。

杜教筛


前置知识:

狄利克雷卷积:

设f,g 是两个数论函数,它们的狄利克雷卷积卷积是:

\(\ (f* g)(n)=\sum_{d|n}f(d)*g(n/d)\)

莫比乌斯反演:

如果:

$?F(n) = \sum_{d|n}f(d) $

那么:

$?f(n) = \sum_{d|n}\mu(d)*F(n/d) $

而我们知道杜教筛是用来解决积性函数前缀和的,也就是:

\(\ S(n)=\sum_{i=1}^nf(i)\)

于是我们考虑狄利克雷卷积,找到另外一个积性函数$?g(x) $:

\(\ \sum_{i=1}^n(f*g)(i)\)

=\(\ \sum_{d=1}^ng(d)*f(n/d)\)

=\(\ \sum_{d=1}^ng(d) \sum_{k=1}^{[n/d]}f(k)\)

=\(\ \sum_{d=1}^ng(d) S([n/d])\)

那么\(S(n)\)怎么表示呢? 你会猛然发现

\(S(n) = \sum_{i=1}^n(f*g)(i)- \sum_{i=2}^n(f*g)(i)\)

那么也就是说只要我们可以知道一个合适的\(g(x)\),使\(g(x)\)的前缀和

可以迅速处理,\(\ \sum_{i=1}^n(f*g)(i)\)可以迅速处理出来,那么

接下来的程序就方便很多了,直接套一波数论分块求解

复杂度\(\ O(n^{2/3} )\) (然而我并不会证明)

然后介绍几个非常有用的\(g\)函数

\(I,id,\epsilon\)

\(I(n)=1\) / \(id(n)=n\) / $ \epsilon(n)=[n==1]$

1.\(\mu*I=\epsilon\)

2.\(\phi*I=id\)

3.\(\mu*id=\phi\)

下面是蒟蒻的杜教筛代码:

#include<bits/stdc++.h>
#include<unordered_map>
typedef long long ll;
using namespace std;
int T , n , num , phi[2700007] , miu[2700007] , book[2700007] , prim[2700007] , summiu[2700007]; 
ll sumphi[2700007] ;
const int MAXN = 2700000 ;
inline int read() { int x=0; char ch=getchar(); while(ch>'9'||ch<'0') ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); return x; }
unordered_map<int,ll> ansphi;
unordered_map<int,int> ansmu;

inline void find()
{
    sumphi[1] = 1 ;
    summiu[1] = 1 ;
    phi[1] = 1 ;
    miu[1] = 1 ;
    for(int i = 2 ; i <= MAXN ; i++ ){
        if( book[i] == 0 ){
            phi[i] = i - 1 ;
            miu[i] = -1 ;
            prim[++num] = i ;
        }
        for(int j = 1 ; j <= num && prim[j] * i <= MAXN; j++ ){
            if( i % prim[j] == 0 )
            {
                book[ i * prim[j] ] = 1 ;
                phi[i*prim[j]] = prim[j] * phi[i] ;
                break ;
            }
            book[ i * prim[j] ] = 1 ;
            phi[i * prim[j]] = phi[i] * (prim[j] - 1) ;
            miu[i * prim[j]] = -miu[i] ;
        }
        sumphi[i] = sumphi[i - 1] + phi[i] ;
        summiu[i] = summiu[i - 1] + miu[i] ;
    }
    return ;
}
ll sum_phi(int x)
{
    if( x <= MAXN ) return sumphi[x] ;
    if( ansphi[x] ) return ansphi[x] ;
    ll res = (ll) x * ( x + 1 ) / 2 ;
    int r ;
    for(register int l = 2 ; l <= x ; l = r + 1 ){
        r = x / ( x  / l ) ;
        res -= 1ll * ( r - l + 1 ) * sum_phi(x / l) ;
    }
    return ansphi[x] = res ;
}
int sum_miu(int x)
{
    if( x <= MAXN ) return summiu[x] ;
    if( ansmu[x] ) return ansmu[x] ;
    int res = 1 ;
    int r ;
    for(register int l = 2 ; l <= x ; l = r + 1 ){
        r = x / ( x / l ) ;
        res -= ( r - l + 1 ) * sum_miu(x / l) ;
    }
    return ansmu[x] = res ;
}
int main()
{
    find() ;
    T = read() ;
    while( T-- )
    {
        n = read() ;
        printf("%lld %d\n" , sum_phi(n) , sum_miu(n) ) ;
    }
    return 0 ;
}

然后还有几道例题:

1.p3455

带题解

2.p3768

带题解

以上是关于杜教筛的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 3944 Sum —— 杜教筛

杜教筛题表(已完成)

杜教筛学习笔记

hdu5608杜教筛

杜教筛

杜教筛