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 最小公倍数计数的主要内容,如果未能解决你的问题,请参考以下文章