1222 最小公倍数计数
定义F(n)表示最小公倍数为n的二元组的数量。
即:如果存在两个数(二元组)X,Y(X <= Y),它们的最小公倍数为N,则F(n)的计数加1。
例如:F(6) = 5,因为[2,3] [1,6] [2,6] [3,6] [6,6]的最小公倍数等于6。
给出一个区间[a,b],求最小公倍数在这个区间的不同二元组的数量。
例如:a = 4,b = 6。符合条件的二元组包括:
[1,4] [2,4] [4,4] [1,5] [5,5] [2,3] [1,6] [2,6] [3,6] [6,6],共10组不同的组合。
输入
输入数据包括2个数:a, b,中间用空格分隔(1 <= a <= b <= 10^11)。
输出
输出最小公倍数在这个区间的不同二元组的数量。
输入样例
4 6
输出样例
10
SilverNebula的题解
这种题显然要莫比乌斯反演!
先不考虑(x≤y),最后答案加上(b-a+1)再除(2)即可。
然后([a,b])又可以变成区间([1,b])的答案减([1,a-1])的答案。
设:
[ans(n)=sum_{i=1}^{n} sum_{j=1}^{n} [frac{i*j}{gcd(i,j)}le n]
]
那么(ans(b)-ans(a-1))就是最终答案
尝试化简上面的式子:
[sum_{i=1}^{n} sum_{j=1}^{n} [frac{i*j}{gcd(i,j)}le n]\
=sum_{d=1}^{n} sum_{i=1}^{frac{n}{d}} sum_{j=1}^{frac{n}{d}} [i*jlefrac{n}{d}] [gcd(i,j)=1]\
=sum_{d=1}^{n} sum_{k=1}^{frac{n}{d}} mu(k) sum_{i=1}^{frac{n}{dk}} sum_{j=1}^{frac{n}{dk}} [i*k*j*klefrac{n}{d}]\
=sum_{k=1}^{n} mu(k) sum_{d=1}^{frac{n}{k}} sum_{i=1}^{frac{n}{dk}} sum_{j=1}^{frac{n}{dk}} [i*j*dlefrac{n}{k^2}]
]
显然(d)和(k)值大到一定程度,最后面就是0了,所以我们可以缩小求和上界:
[sum_{k=1}^{sqrt n} mu(k) sum_{d=1}^{frac{n}{k^2}} sum_{i=1}^{frac{n}{dk^2}} sum_{j=1}^{frac{n}{dk^2}} [i*j*dlefrac{n}{k^2}]
]
这个范围很友好,我们可以枚举(mu(k)),求满足条件的((d,i,j))三元组数量。
需要求的三元组是无序的,为了不重不漏地计数,我们可以分别求出有序(单调上升)的三元组数量,对于其中三个数各不同的、有两个数相同的、三个数都相同的分别计数,然后乘以对应的排列数即可。
时间复杂度(O(sum_{k=1}^{n^{frac 12}}(frac n{k^2})^frac 13)),积分近似一下是(O(n^frac 12))
#include<bits/stdc++.h>
#define il inline
#define co const
template<class T>T read(){
T data=0,w=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
return data*w;
}
template<class T>il T read(T&x) {return x=read<T>();}
typedef long long LL;
co int N=316300;
int pri[N],tot,mu[N];
void sieve(){
pri[1]=mu[1]=1;
for(int i=2;i<N;++i){
if(!pri[i]) pri[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*pri[j]<N;++j){
pri[i*pri[j]]=1;
if(i%pri[j]==0){
mu[i*pri[j]]=0;
break;
}
mu[i*pri[j]]=-mu[i];
}
}
}
LL solve(LL n){
if(!n) return 0;
LL ans=0;
for(LL k=1;k*k<=n;++k)if(mu[k]){
LL lim=n/(k*k),sum=0;
for(LL i=1;i*i*i<=lim;++i){
for(LL j=i+1;j*j*i<=lim;++j)
sum+=(lim/(i*j)-j)*6+3;
sum+=(lim/(i*i)-i)*3+1;
}
ans+=mu[k]*sum;
}
return ans;
}
int main(){
sieve();
LL a=read<LL>(),b=read<LL>();
printf("%lld
",(solve(b)-solve(a-1)+b-a+1)/2);
return 0;
}