杜教筛
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
以上是关于杜教筛的主要内容,如果未能解决你的问题,请参考以下文章