莫比烏斯反演:
若:
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;
}~