51Nod1222 最小公倍数计数

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*j\le\frac{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*k\le\frac{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*d\le\frac{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*d\le\frac{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\n",(solve(b)-solve(a-1)+b-a+1)/2);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/11104913.html