链接
http://www.lydsy.com/JudgeOnline/problem.php?id=3944
求积性函数前N项的线性算法
积性函数是数论函数,且满足当
gcd(a,b)=1
时,
f(a)f(b)=f(ab)
依据这一点,可以得到积性函数的线性筛法。
显然,当
x
是素数时,
当
x
不是素数时,令
具体地,边筛素数边做。对于每个数
i
,枚举比它的最小质因子小的所有素数
这个方法有局限性,当
gcd(a,b)=a
时,如果不能根据定义方便的求出
f(ab)
的值,那就不能做了。
那么可以根据积性,把
x
分解成
狄利克雷(dirichlet)卷积
两个数论函数
f(x)、g(x)
其
dirichlet
卷积:
dirichlet 卷积的性质:
交换律、结合律、分配律、
f∗ϵ=f
特殊性质: μ∗1=ϵ
杜教筛
杜教筛,我也不知道怎么得出来的,只会背公式。
欲求
有一个公式
对于不同的 f() 函数,要选择不同的 g() 来卷。
这样就能够得到 S 的递推式,直接记忆化搜索的时间复杂度是
预处理前 N23 项再记忆化搜索的时间复杂度是 O(N23)
证明莫比乌斯反演定理
学了
drichlet
卷积之后,证明莫比乌斯反演就容易了。
莫比乌斯反演定理:
预备知识:
单位函数 ϵ={01i>1i=1
首先有个性质 f∗ϵ=f
充分性:
卷 μ
必要性:
卷 1
因此 f=g∗1 和 g=f∗μ 互为充要条件
题解
呼~终于能够写题解了。
对于
由杜教筛的式子(卷 1 )得到
有个性质 ∑d|nϕ(d)=n ,所以
同理对于 μ() ,两边也卷 1
代码
//杜教筛
#include <cstdio>
#include <algorithm>
#define maxn 1700000
#define ll long long
using namespace std;
ll N, mu[maxn], phi[maxn], f[maxn], g[maxn], prime[maxn], mark[maxn], T, vis[maxn];
void init()
{
ll i, j;
phi[1]=mu[1]=1;
for(i=2;i<maxn;i++)
{
if(!mark[i])prime[++prime[0]]=i, phi[i]=i-1, mu[i]=-1;
for(j=1;j<=prime[0] and i*prime[j]<maxn;j++)
{
mark[i*prime[j]]=1;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
phi[i*prime[j]]=phi[i]*phi[prime[j]];
}
}
for(i=2;i<maxn;i++)phi[i]+=phi[i-1],mu[i]+=mu[i-1];
}
inline ll getf(ll n){return n<maxn?phi[n]:f[N/n];}
inline ll getg(ll n){return n<maxn?mu[n]:g[N/n];}
void calc(ll n)
{
ll i, t=N/n, last;
if(n<maxn)return;
if(vis[t]!=T)vis[t]=T;else return;
f[t]=n*(1+n)/2;
g[t]=1;
for(i=2;i<=n;i=last+1)
{
last=n/(n/i);
calc(n/i);
f[t]-=getf(n/i)*(last-i+1);
g[t]-=getg(n/i)*(last-i+1);
}
}
int main()
{
init();
for(scanf("%lld",&T);T;T--)
{
scanf("%lld",&N);
calc(N);
printf("%lld %lld\n",getf(N),getg(N));
}
return 0;
}