终于想起来要学这个了,但是似乎还没学完?
先写一部分那就。
注意:在此讨论的函数的定义域均在正整数上讨论
积性函数定义:
设有
f(x)
f
(
x
)
满足:若
则有
那么我们称 f(x) f ( x ) 为积性函数。
如:
1.常数函数 l(n)=1 l ( n ) = 1
2. d(n) d ( n ) :n的因子个数之和。
3. σ(n) σ ( n ) :n的各因子之和。
4. μ(n) μ ( n ) :莫比乌斯函数。
5. ϕ(n) ϕ ( n ) :欧拉函数。
狄利克雷卷积
设有函数
f(x)
f
(
x
)
和
g(x)
g
(
x
)
,其狄利克雷卷积为:
性质:两个积性函数的狄利克雷卷积也是积性的。
杜教筛
作用:在
O(n23)
O
(
n
2
3
)
的时间里求某些积性函数的前缀和
即:
所以显而易见所能处理的n的数量级就可增加一些了。
来看一一下具体是怎么操作的。
设我们要求
sum(x)=∑nif(i)
s
u
m
(
x
)
=
∑
i
n
f
(
i
)
且
f(n)
f
(
n
)
是积性函数。
那么我们找到一个同样是积性的函数
g(n)
g
(
n
)
和
f(n)
f
(
n
)
卷一卷。
有:
那么卷积的前缀和为:
变换枚举角度为:
在稍微变形移一下项有:
我们的目标是求 sum(n) s u m ( n ) ,假如卷积函数的前缀和很好求的话,那么我们只需要处理最右边那个东西了。
最常见的例子是求:
易知有:
这个可以看成是 μ μ 函数和常数函数的卷积
所以按照上述推导的公式,我们有:
看这个式子的结构就具有递归性,右边的那个就可以分块处理了。
来一发板子题。Sum HYSBZ - 3944
就是求欧拉函数和莫比乌斯函数的前缀和,n的范围是int范围。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#define maxx 2500005
using namespace std;
int prime[maxx],cnt;
bool p[maxx];
long long mu[maxx],phi[maxx];
map<long long ,long long>M,P;//用map来记忆化
void init()
{
p[1]=mu[1]=phi[1]=1;
for(int i=2;i<maxx;i++)
{
if(!p[i])
{
prime[cnt++]=i;
mu[i]=-1;
phi[i]=i-1;
}
for(int j=0;j<cnt&&i*prime[j]<N;j++)
{
p[i*prime[j]]=true;
if(i%prime[j])
{
mu[i*prime[j]]=-mu[i];
phi[i*prime[j]]=phi[i]*phi[prime[j]];
}
else
{
mu[i*prime[j]]=0;
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
}
}
/*for(int i=1;i<100;i++)
cout<<i<<" "<<phi[i]<<endl;*/
for(int i=1;i<N;i++)
mu[i]+=mu[i-1],phi[i]+=phi[i-1];
}
long long Phi(long long x)
{
if(x<N)return phi[x];
if(P[x])return P[x];
long long res=0;
for(long long i=2,last;i<=x;i=last+1)
{
last=x/(x/i);
res+=(last-i+1)*Phi(x/i);
}
return P[x]=x*(x+1)/2-res;
}
long long Mu(long long x)
{
if(x<N)return mu[x];
if(M[x])return M[x];
long long res=0;
for(long long i=2,last;i<=x;i=last+1)
{
last=x/(x/i);
res+=(last-i+1)*Mu(x/i);
}
return M[x]=1-res;
}
int main()
{
init();
int t;
cin>>t;
while(t--)
{
int n;
scanf("%d",&n);
printf("%lld %lld\n",Phi(n),Mu(n));
}
return 0;
}
但是这种写法T了…….应该是记忆化的问题。所以这个记忆化得在处理一下。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#define maxx 2500005
using namespace std;
int prime[maxx],cnt;
bool p[maxx];
long long mu[maxx],phi[maxx];
long long ans1[maxx],ans2[maxx];
long long n;
void init()
{
p[1]=mu[1]=phi[1]=1;
for(int i=2;i<maxx;i++)
{
if(!p[i])
{
prime[cnt++]=i;
mu[i]=-1;
phi[i]=i-1;
}
for(int j=0;j<cnt&&i*prime[j]<maxx;j++)
{
p[i*prime[j]]=true;
if(i%prime[j])
{
mu[i*prime[j]]=-mu[i];
phi[i*prime[j]]=phi[i]*phi[prime[j]];
}
else
{
mu[i*prime[j]]=0;
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
}
}
/*for(int i=1;i<100;i++)
cout<<i<<" "<<phi[i]<<endl;*/
for(int i=1;i<maxx;i++)
mu[i]+=mu[i-1],phi[i]+=phi[i-1];
}
void work(long long x)
{
if(x<maxx)return ;
int t=n/x;
if(p[t])return ;
p[t]=true;
ans1[t]=(long long)x*(x+1)/2;
ans2[t]=1;
for(long long i=2,last;i<=x;i=last+1)
{
last=x/(x/i);
if(x/i<maxx)
{
ans1[t]-=(last-i+1)*phi[x/i];
ans2[t]-=(last-i+1)*mu[x/i];
}
else
{
work(x/i);
ans1[t]-=(last-i+1)*ans1[n/(x/i)];
ans2[t]-=(last-i+1)*ans2[n/(x/i)];
}
}
}
int main()
{
init();
int t;
cin>>t;
while(t--)
{
scanf("%lld",&n);
if(n<maxx)
printf("%lld %lld\n",phi[n],mu[n]);
else
{
work(n);
printf("%lld %lld\n",ans1[1],ans2[1]);
}
}
return 0;
}