@xiaoziyao
2021-05-02T09:02:43.000000Z
字数 10988
阅读 1307
数学
学习笔记
upd:内容比较老旧,但是不想更新了,到时候会写在另一个博客里。
主要是上一篇字数太多,太卡了,于是杜教筛就换了一篇写 多水了一篇博客
先讲一下前置知识:
莫比乌斯函数,即
狄利克雷卷积,可以表示为
狄利克雷卷积满足交换律和结合律,且存在单位元
简证为单位元:
例子:
重要的数论函数中互相转化:
在莫比乌斯反演的应用题中,经常会有的形式,但是的复杂度有时候仍然满足不了需求
为了解决这一问题,我们就要用到整除分块
很容易发现很多的值是一样的,且所有的为一个不下降子序列,呈块状分布
通过简单的计算可以得到,对于每个起点为的块,它的值为,终点为,然后我们就可以用的算法计算的值了
代码:
int l=1,r,ans=0;
if(n>m)
swp(n,m);
while(l<=n){
r=min(n/(n/l),mm/(m/l));
ans+=(r-l+1)*(n/l);
l=r+1;
}
我们要筛一个积性函数的前缀和,杜教筛可以帮我们做到(当然可以记忆化常数优化)
我们设,其中为积性函数
我们找一个可以与卷积的积性函数,推一下它们的狄利克雷卷积前缀和:
我们可以递归求解,而且可以设一个边界,如果则直接输出预处理的前缀和
然后我们考虑的值:
此时,如果找到一个合适的使与的前缀和可以很容易处理,那么就可以用数论分块递归求解
我们可以由这个表得出一些数论函数相应的函数:
即:
1.例题1:P4213 【模板】杜教筛(Sum)
题意:共组数据,每组数据给定,求和
我们根据上面的表很容易写出来,因此没有什么推导过程
代码:
#include<stdio.h>
#include<map>
using namespace std;
const long long maxn=3000005;
long long i,j,k,m,n,T,cnt;
long long a[maxn],p[maxn],varphi[maxn],mu[maxn],sumvarphi[maxn],summu[maxn];
map<long long,long long>ansvarphi,ansmu;
long long getvarphi(long long n){
if(n<maxn)
return sumvarphi[n];
if(ansvarphi.count(n))
return ansvarphi[n];
long long l=2,r,res=n*(n+1)/2;
while(l<=n){
r=n/(n/l);
res-=(r-l+1)*getvarphi(n/l);
l=r+1;
}
return ansvarphi[n]=res;
}
long long getmu(long long n){
if(n<maxn)
return summu[n];
if(ansmu.count(n))
return ansmu[n];
long long l=2,r,res=1;
while(l<=n){
r=n/(n/l);
res-=(r-l+1)*getmu(n/l);
l=r+1;
}
return ansmu[n]=res;
}
int main(){
p[1]=varphi[1]=mu[1]=1;
for(i=2;i<maxn;i++){
if(p[i]==0)
a[++cnt]=i,varphi[i]=i-1,mu[i]=-1;
for(j=1;j<=cnt;j++){
if(i*a[j]>=maxn)
break;
p[i*a[j]]=1;
if(i%a[j]==0){
varphi[i*a[j]]=varphi[i]*a[j],mu[i*a[j]]=0;
break;
}
varphi[i*a[j]]=varphi[i]*(a[j]-1),mu[i*a[j]]=-mu[i];
}
}
for(i=1;i<maxn;i++)
sumvarphi[i]=sumvarphi[i-1]+varphi[i],summu[i]=summu[i-1]+mu[i];
scanf("%lld",&T);
while(T--){
scanf("%lld",&n);
printf("%lld %lld\n",getvarphi(n),getmu(n));
}
return 0;
}
2.例题2:简单的数学题
题意:求
数据范围:
相信大家都看了之前莫比乌斯反演的讲解,因此这里过程简单一点:
代码:
#include<stdio.h>
#include<map>
using namespace std;
const long long maxn=5000005;
long long i,j,k,m,n,mod,cnt,inv6;
long long a[maxn],p[maxn],varphi[maxn],S[maxn];
map<long long,long long>ans;
inline long long getsum(long long n){
return n*(n+1)/2%mod;
}
inline long long calc(long long n){
return n*(n+1)%mod*(2*n+1)%mod*inv6%mod;
}
long long ksm(long long a,long long b,long long mod){
long long res=1;
while(b){
if(b&1)
res=(res*a)%mod;
a=(a*a)%mod,b>>=1;
}
return res;
}
long long getS(long long n){
if(n<maxn)
return S[n];
if(ans.count(n))
return ans[n];
long long l=2,r,res=getsum(n)*getsum(n)%mod;
while(l<=n){
r=n/(n/l);
res=((res-(calc(r)-calc(l-1)+mod)%mod*getS(n/l)%mod)%mod+mod)%mod;
l=r+1;
}
res=(res%mod+mod)%mod;
return ans[n]=res;
}
long long getans(long long n){
long long l=1,r,res=0;
while(l<=n){
r=n/(n/l);
res=(res+(getS(r)-getS(l-1)+mod)%mod*getsum(n/l)%mod*getsum(n/l)%mod)%mod;
l=r+1;
}
res=(res%mod+mod)%mod;
return res;
}
int main(){
scanf("%lld%lld",&mod,&n);
inv6=ksm(6,mod-2,mod);
p[1]=varphi[1]=1;
for(i=2;i<maxn;i++){
if(p[i]==0)
a[++cnt]=i,varphi[i]=i-1;
for(j=1;j<=cnt;j++){
if(i*a[j]>=maxn)
break;
p[i*a[j]]=1;
if(i%a[j]==0){
varphi[i*a[j]]=varphi[i]*a[j];
break;
}
varphi[i*a[j]]=varphi[i]*(a[j]-1);
}
}
for(i=1;i<maxn;i++)
S[i]=(S[i-1]+varphi[i]%mod*i%mod*i%mod)%mod;
printf("%lld\n",getans(n));
return 0;
}