Luogu P4450 双亲数
Description:
给定 \(A,B,d\) ,求满足$1\le a\le A,1\le b\le B $ 且 \(\gcd(a,b)=d\) 的数对数
Solution
考虑莫比乌斯反演
原式可化为
\[\sum_{i=1}^A\sum_{j=1}^B\gcd(i,j)=d
\]
根据套路,我们先给两边除以 \(d\),化成
\[\sum_{i=1}^{\left\lfloor\tfrac A d\right\rfloor}\sum_{j=1}^{\left\lfloor\tfrac B d\right\rfloor}\gcd(i,j)=1
\]
为什么要化成这个形式呢?
因为根据莫比乌斯函数的性质
\[\sum_{d\mid n} \mu(d) = [n=1]
\]
这个式子可以化成
\[\sum_{i=1}^{\left\lfloor\tfrac A d\right\rfloor}\sum_{j=1}^{\left\lfloor\tfrac B d\right\rfloor}\sum_{k\mid \gcd(i,j)} \mu(k)
\]
我们考虑交换枚举顺序,先枚举 \(d\),原式就可以化成
\[\sum_{k=1}^{\min(\frac A d,\frac B d)} \mu(k)\sum_{i=1}^{\left\lfloor\tfrac A d\right\rfloor}\sum_{j=1}^{\left\lfloor\tfrac B d\right\rfloor} [k\mid\gcd(i,j)]
\]
\[\sum_{k=1}^{\min(\frac A d,\frac B d)}\mu(k)\left\lfloor\tfrac A {kd}\right\rfloor\left\lfloor\tfrac B {kd}\right\rfloor
\]
对于这个式子,\(\mu\) 函数可以线性预处理,分数可以用整除分块来做,时间复杂度是\(\Theta(\sqrt n)\)
Code
/*If you are full of hope,you will be invincible*/
#include <ctime>
#include <cstdio>
#include <random>
#include <cstring>
#include <iostream>
#include <algorithm>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#include<ext/pb_ds/priority_queue.hpp>
#define ri register int
typedef long long ll;
std::mt19937 hpy(time(nullptr)+(unsigned long long)(new char));
namespace IO{
constexpr int HL=1<<20;
char buf[HL],*t1=buf,*t2=buf;
#ifndef ONLINE_JUDGE
#define getc() getchar()
#else
#define getc() t1==t2&&(t2=(t1=buf)+fread(buf,1,HL,stdin),t1==t2)?EOF:*t1++
#endif
inline int read(){static int an,f;an = 0,f = 1;static char ch;ch=getc();
while(!isdigit(ch))ch=='-'?f=-1:1,ch=getc();
while(isdigit(ch))an=(an<<3)+(an<<1)+(ch^48),ch=getc();
return an*f;}
char buff[HL],*T=buff;
void flush(){fwrite(buff,1,T-buff,stdout);T=buff;}
inline void putc(char ch){if(T==buff+HL)flush();*T++=ch;}
template<typename Tp>
inline void print(Tp x){if(x<0)putc('-'),x=-x;if(!x)return putc('0'),void();
static int st[20],tp;while(x)st[++tp]=x%10,x/=10;while(tp)putc(st[tp]^48),--tp;}
}
using IO::read;
using IO::print;
using IO::putc;
constexpr int inf = 0x3f3f3f3f,N = 1e6 + 10;
int vis[N],n,m,cnt;
ll pri[N],sum[N],mu[N];
inline void Sieve() {
mu[1] = 1;
for(ri i = 2;i <= N;++i) {
if(!vis[i]) vis[i] = 1,pri[++cnt] = i,mu[i] = -1;
for(ri j = 1;j <= cnt && i*pri[j] <= N;++j) {
vis[i*pri[j]] = 1;
if(i%pri[j] == 0) break;
mu[i*pri[j]] = -mu[i];
}
}//线性筛μ
for(ri i = 1;i <= N;++i) sum[i] = mu[i] + sum[i-1];//用前缀和处理
}
ll Solve(int a,int b,int d){
int mx = std::min(a,b);
ll ans = 0;
for(ri l = 1,r;l <= mx;l = r + 1) {//整除分块
r = std::min(a/(a/l),b/(b/l));
ans += (sum[r]-sum[l-1])*(1ll*a/(1ll*l*d)) * (1ll*b/(1ll*l*d));
}
return ans;
}
int main() {
Sieve();
int a=read(),b=read(),d=read();
print(Solve(a,b,d));
return IO::flush(),0;
}