NOIP模拟赛 太阳神

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/83271160

太阳神拉很喜欢最小公倍数,有一天他想到了一个关于最小公倍
数的题目。
求满足如下条件的数对(a,b)对数:a,b 均为正整数且a,b<=n 而
lcm(a,b)>n。其中的lcm 当然表示最小公倍数。答案对1,000,000,007
取模
n<=10000000000

换个方向,求lcm(a,b)<=n的对数。
设gcd(a,b) = d

sigma([a/d * b/d * d <= n && gcd(a/d,b/d)=1])
=sigma(mu[c] * sigma([a/d * b/d * d <= n && c|gcd(a/d,b/d)]))
=sigma(mu[c] * sigma([a/d/c * b/d/c * d<=n]))
然后你会惊奇的发现a/d和b/d和d这3个数已经只有积小于n/c/c这唯一的一层关系了,
然后枚举这3个数中最小的,第二小的,算出最大的数的范围,然后就可以计算贡献了。
枚举3个数是O(n2/3)的,外层枚举c,c>1时的复杂度其实是高阶小量,不用管,总复杂度O(n2/3)。
AC Code:

//%%%zjx dalao
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 100005
#define mod 1000000007
#define LL long long
using namespace std;
LL n,ans;
int mu[maxn],pr[maxn],cnt_pr,vis[maxn];
int main()
{
	scanf("%lld",&n);
	mu[1] = 1;
	int lim = int(sqrt(n)+0.5);
	for(int i=2;i<=lim;i++)
	{
		if(!vis[i]) pr[cnt_pr++] = i , mu[i] = -1;
		for(int j=0;j<cnt_pr && i*pr[j]<=lim;j++)
		{
			vis[i * pr[j]] = 1;
			if(i % pr[j] == 0){mu[i * pr[j]] = 0; break;}
			mu[i * pr[j]] = -mu[i];
		}
	}
	for(int d=1;d<=lim;d++)
	{
		LL sum = n / d / d , ret = 0;
		for(int a=1;1ll*a*a*a<=sum;a++)
			for(int b=a;1ll*b*b<=sum/a;b++)
			{
				LL c = sum / a / b - b;
				if(a == b) ret = (ret + 1 + c * 3) % mod;
				else ret = (ret + 3 + c * 6) % mod;
			}
		ans = (ans + mu[d] * ret) % mod;
	}
	n%=mod;
	printf("%lld\n",((1ll*n*n%mod-ans)%mod+mod)%mod);
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/83271160