基本公式
莫比乌斯函数
原式子
[mu(u)= egin{cases} 1&n=1\ 0&n ext{含有平方因子}\ (-1)^k&k ext{为}n ext{的本质不同的质因子个数}\ end{cases}
]
代码实现(线性筛)
void get_mu(){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!flag[i]) prime[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&prime[j]*i<=n;j++){
flag[prime[j]*i]=1;
if(i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]-=mu[i];
}
}
}
奇妙性质({color{#8B0000}{large star}})
[sum_{dmid n} mu(d) =varepsilon(n)= egin{cases} 1&n=1\ 0 &n
eq 1\ end{cases}
]
(因此运用到题里后经常需要对莫比乌斯函数求前缀和)
莫比乌斯反演({color{#8B0000}{large star}})
[ ext{如果有} f(n)=sum_{dmid n} g(d) ext{ 则} g(n)=sum_{dmid n} mu(d) f( frac{n}{d})
]
[ ext{如果有} f(n)=sum_{nmid d} g(d) ext{ 则} g(n)=sum_{nmid d} mu( frac{n}{d}) f(d)
]
运用技巧
数论分块({color{#8B0000}{large star}})
解说
[ ext{有时我们会遇到形如} sum lfloor frac{n}{i}
floor ext{的式子且} Theta (n) ext{过不去,这时候就会用到数列分块。}
]
[ ext{显然对于一段数} lfloor frac{n}{i}
floor ext{都是相等的,那么我们就可以将其分到一块里一并计算。}
]
[ ext{那么现在我们就需要找到一个最大的}j ext{使得} lfloor frac{n}{i}
floor = lfloor frac{n}{j}
floor ext{,结论是此时} j= left lfloor frac{n}{left lfloor frac{n}{i}
ight
floor }
ight
floor ext{。下面是证明。}
]
[egin{aligned} &leftlfloorfrac{n}{i}
ight
floor leq frac{n}{i}\ implies &leftlfloorfrac{n}{ leftlfloorfrac{n}{i}
ight
floor }
ight
floor geq leftlfloorfrac{n}{ frac{n}{i} }
ight
floor = leftlfloor i
ight
floor=i \ implies &ileq leftlfloorfrac{n}{ leftlfloorfrac{n}{i}
ight
floor }
ight
floor=j\ &&square end{aligned}
]
此时时间复杂度为(Theta(sqrt n))
当然还有二维版本的
[sum_{i=1}^{min (n,m)}leftlfloorfrac{n}{i}
ight
floorleftlfloorfrac{m}{i}
ight
floor
]
此时将r = n/(n/l)
替换成 r = min(n/(n/l), m/(m/l))
就完了
(以上证明引自OI Wiki)
代码实现
(以洛谷P2261为例,很裸的数论分块)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,k;
ll ans,l,r;
int main(){
scanf("%d%d",&n,&k);
ans=(ll)n*(ll)k;
l=1;
while(l<=n){
if(k/l) r=min(k/(k/l),(ll)n);
else r=n;
ans-=(k/l)*(r-l+1)*(l+r)/2;
l=r+1;
}
printf("%lld
",ans);
return 0;
}
例题
解说
题目让我们求的是
[sum_{i=a}^{b}sum_{j=c}^{d} varepsilon (gcd(i,j)==k)qquad
]
可以拆成四块进行求解,每一块都形如
[sum_{i=1}^{n}sum_{j=1}^{m}varepsilon (gcd(i,j)==k)
]
上式可化为
[sum_{i=1}^{lfloorfrac{n}{k}
floor}sum_{j=1}^{lfloorfrac{m}{k}
floor}varepsilon(gcd(i,j))
]
即
[displaystylesum_{i=1}^{lfloorfrac{n}{k}
floor}sum_{j=1}^{lfloorfrac{m}{k}
floor}sum_{dmid gcd(i,j)}mu(d)
]
变换求和顺序得
[displaystylesum_{d=1}mu(d)sum_{i=1}^{lfloorfrac{n}{k}
floor}varepsilon (dmid i)sum_{j=1}^{lfloorfrac{m}{k}
floor}varepsilon (dmid j)
]
(1 sim lfloor frac{n}{d} floor)中(d)的倍数一共(lfloor frac{n}{kd} floor)个,所以原式还可以化为
[displaystylesum_{d=1}mu(d)lfloorfrac{n}{kd}
floorlfloorfrac{m}{kd}
floor
]
符合数论二维分块的形式,可以 (Theta(sqrt n)) 搞掉了
代码
#include<bits/stdc++.h>
using namespace std;
const int lzw=5e4+3;
int tot,prime[lzw],mu[lzw],a,b,c,d,t,k;
bool flag[lzw];
void get_mu(){
mu[1]=1;
for(int i=2;i<=lzw-3;i++){
if(!flag[i]) prime[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&prime[j]*i<=lzw-3;j++){
flag[prime[j]*i]=1;
if(i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]-=mu[i];
}
}
for(int i=1;i<=lzw-3;i++) mu[i]+=mu[i-1];
}
int solve(int n,int m){
int res=0,l=1,r;
while(l<=min(n,m)){
r=min(n/(n/l),m/(m/l));
res+=(mu[r]-mu[l-1])*(n/l)*(m/l);
l=r+1;
}
return res;
}
int main(){
get_mu();
scanf("%d",&t);
while(t--){
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
printf("%d
",solve(b/k,d/k)-solve(b/k,(c-1)/k)-solve((a-1)/k,d/k)+solve((a-1)/k,(c-1)/k));
}
return 0;
}
例题2
解说
上面的例题似乎并没有反演,只是用了一下莫比乌斯函数的性质,这里再放一个反演的例题。
题目让我们求的答案为
[ans=sum_{i=1}^{N}sum_{j=1}^{M} varepsilon(gcd(i,j)==prime)
]
不妨设
[f(n)= sum_{i=1}^{N}sum_{j=1}^{M} varepsilon(gcd(i,j)==n)
]
那么答案可以化为
[ans=sum_{pin prime}f(p)
]
再增设一个函数
[g(n)=sum_{nmid d} f(d)
]
显然(好像不这么显然但是稍微看看能弄明白)它还可以写成另一种形式
[g(n)=leftlfloor frac{N}{n}
ight
floor leftlfloor frac{M}{n}
ight
floor
]
这个时候我们就珂以反演
[ecause g(n)=sum_{nmid d} f(d)
]
[ herefore f(n)=sum_{nmid d} mu(frac{d}{n})g(d)
]
那么我们就珂以接着推答案式子
[ans=sum_{pin prime}f(p)=sum_{pin prime}sum_{pmid d} mu(frac{d}{p})g(d)
]
即
[ans=sum_{pin prime}sum_{pmid d} mu(frac{d}{p})leftlfloor frac{N}{d}
ight
floor leftlfloor frac{M}{d}
ight
floor
]
我们把后一个求和换成枚举(d)的形式,显然在(d)大于(N)或(M)时后面的求和全都是(0),因此我们只枚举到(min (N,M))
[ans=sum_{pin prime}sum_{d=kp,kin N^+}^{min(N,M)} mu(frac{d}{p})leftlfloor frac{N}{d}
ight
floor leftlfloor frac{M}{d}
ight
floor
]
换一个枚举顺序得
[ans=sum_{d=1}^{min(N,M)}sum_{pin prime,pmid d}mu(frac{d}{p})leftlfloor frac{N}{d}
ight
floor leftlfloor frac{M}{d}
ight
floor
]
由于第二个求和已经和(d)无关,我们可以提一下公因式
[ans=sum_{d=1}^{min(N,M)}leftlfloor frac{N}{d}
ight
floor leftlfloor frac{M}{d}
ight
floorsum_{pin prime,pmid d}mu(frac{d}{p})
]
这个时候我们就可以用数列分块搞它了。预处理的时候多处理一下(sum_{pin prime,pmid d}mu(frac{d}{p}))即可
代码
#include<bits/stdc++.h>
using namespace std;
const int lzw=1e7+3;
typedef long long ll;
int n,m,t,prime[lzw],tot,mu[lzw],g[lzw];
bool flag[lzw];
ll sum[lzw];
void get_mu(){
flag[1]=1,mu[1]=1;
for(int i=2;i<=lzw-3;i++){
if(!flag[i]) flag[i]=1,prime[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&(ll)prime[j]*i<=(ll)lzw-3;j++){
flag[prime[j]*i]=1;
if(i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]-=mu[i];
}
}
for(int i=1;i<=tot;i++)
for(int j=1;(ll)j*prime[i]<=(ll)lzw-3;j++)
g[j*prime[i]]+=mu[j];
for(int i=1;i<=lzw-3;i++) sum[i]=sum[i-1]+g[i];
}
ll solve(int n,int m){
ll res=0;
int l=1,r;
while(l<=min(n,m)){
r=min(n/(n/l),m/(m/l));
res+=(ll)(n/l)*(m/l)*(sum[r]-sum[l-1]);
l=r+1;
}
return res;
}
int main(){
get_mu();
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
printf("%lld
",solve(n,m));
}
return 0;
}
幸甚至哉,歌以咏志。