51Nod1222 最小公倍数计数

Posted autoint

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 [\\fraci*jgcd(i,j)\\le n] \\]
那么\\(ans(b)-ans(a-1)\\)就是最终答案

尝试化简上面的式子:
\\[ \\sum_i=1^n \\sum_j=1^n [\\fraci*jgcd(i,j)\\le n]\\=\\sum_d=1^n \\sum_i=1^\\fracnd \\sum_j=1^\\fracnd [i*j\\le\\fracnd] [gcd(i,j)=1]\\=\\sum_d=1^n \\sum_k=1^\\fracnd \\mu(k) \\sum_i=1^\\fracndk \\sum_j=1^\\fracndk [i*k*j*k\\le\\fracnd]\\=\\sum_k=1^n \\mu(k) \\sum_d=1^\\fracnk \\sum_i=1^\\fracndk \\sum_j=1^\\fracndk [i*j*d\\le\\fracnk^2] \\]
显然\\(d\\)\\(k\\)值大到一定程度,最后面就是0了,所以我们可以缩小求和上界:
\\[ \\sum_k=1^\\sqrt n \\mu(k) \\sum_d=1^\\fracnk^2 \\sum_i=1^\\fracndk^2 \\sum_j=1^\\fracndk^2 [i*j*d\\le\\fracnk^2] \\]
这个范围很友好,我们可以枚举\\(\\mu(k)\\),求满足条件的\\((d,i,j)\\)三元组数量。
需要求的三元组是无序的,为了不重不漏地计数,我们可以分别求出有序(单调上升)的三元组数量,对于其中三个数各不同的、有两个数相同的、三个数都相同的分别计数,然后乘以对应的排列数即可。

时间复杂度\\(O(\\sum_k=1^n^\\frac 12(\\frac nk^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;

以上是关于51Nod1222 最小公倍数计数的主要内容,如果未能解决你的问题,请参考以下文章

51nod1222 最小公倍数计数

51Nod1222 最小公倍数计数 数论 Min_25 筛

51nod1222 最小公倍数计数

51nod 1222 最小公倍数计数莫比乌斯反演

51Nod1601 完全图的最小生成树计数 Trie Pruffer编码

51Nod1601 完全图的最小生成树计数