莫比乌斯反演:
若:
F(n)=∑d|n {f(d)}
则:
f(n)=∑d|n {μ(d)*F(⌊n/d⌋)}
或者是:
若:
F(n)=∑n|d {f(d)}
则:
f(n)=∑n|d {μ(d/n)F(d)}
两种情况。
题目:洛谷P3455
莫比乌斯反演模版题:求满足 1≤x≤a,1≤y≤b且 gcd(x,y)=k 的二元组 (x,y) 的数量。
- gcd(x,y)==k 推出 gcd(x/k,y/k)==1;
- 应此我们只需求出在1 到 min(a,b) /k范围内gcd(x,y)==1的二元组即可,即求f(1);
- 设f(d)为gcd(x,y)==d 的二元组数量,F[n]为 F(n)=∑n|d {f(d)},F[n]表示的是 1 到 min(a,b) 所有gcd(x,y) 是n的倍数的二元组数量。
- 因此推出F[n] = (a/n)*(b/n);
- 由反演可得: f(n)=∑n|d {μ(d/n)F(d)} 变换可得 f(1)=∑i=1 {μ[i]*F[i]};
这道题如果跑裸的公式会超时,需要借助整除分块。
整除分块:
很方便的工具 计算sum (1到 n)n/i (整除) ;
int ans=0;
for(int l=1,r;i<=n;l=r+1)
{
r=n/(n/l);
ans+=(r-l+1)*(n/l);
}
代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#define ll long long
using namespace std;
const int N=54321;
ll prime[N],mu[N],sum[N];
bool p[N];
void get_mu() //线性筛,筛选出莫比乌斯函数;
{
int cnt=0;
mu[1]=sum[1]=1;
for(int i=2;i<N;++i){
if(!p[i]){
prime[cnt++]=i;
mu[i]=-1;
}
for(int j=0;j<cnt&&i*prime[j]<N;++j){
p[i*prime[j]]=true;
if(i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
sum[i]=mu[i]+sum[i-1];
}
}
int main()
{
ll t,a,b,k,m;
get_mu();
scanf("%lld",&t);
while(t--){
scanf("%lld%lld%lld",&a,&b,&k);
ll ans=0;
a/=k,b/=k;
if(a>b){
m=a;
a=b;
b=m;
}
for(int l=1,r;l<=a;l=r+1){ // 此处需要理解整除分块;
r=min(a/(a/l),b/(b/l));
ans+=(sum[r]-sum[l-1])*(a/l)*(b/l);
}
printf("%lld\n",ans);
}
return 0;
}~