**题目来源: Project Euler
基准时间限制:6 秒 空间限制:131072 KB 分值: 640 难度:8级算法题**
定义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组不同的组合。
Input
输入数据包括2个数:a, b,中间用空格分隔(1 <= a <= b <= 10^11)。
Output
输出最小公倍数在这个区间的不同二元组的数量。
Input示例
4 6
Output示例
10
可以推导,先考虑大小没有限制的二元组情况:
F′(n)=∑i=1n∑j=1n[ijgcd(i,j)==n]=∑d|n∑d|i∑d|j[ijd==n]=∑d|n∑ind∑jnd[ijd==n]=∑d|n∑ind∑jnd[ij==nd]=∑d|n∑i=1d∑j=1d[ij==d]=∑d|n∑k|d1F′(n)=∑i=1n∑j=1n[ijgcd(i,j)==n]=∑d|n∑d|i∑d|j[ijd==n]=∑d|n∑ind∑jnd[ijd==n]=∑d|n∑ind∑jnd[ij==nd]=∑d|n∑i=1d∑j=1d[ij==d]=∑d|n∑k|d1
但是这种是对于二元组没有大小关系限制时的答案。
设有d(n)=∑d|nd(n)=∑d|n即因子个数函数。
现在:
∑i=1nF(i)=∑i=1n∑j|id(j)=∑j=1nd(j)⌊nj⌋∑i=1nF(i)=∑i=1n∑j|id(j)=∑j=1nd(j)⌊nj⌋
我们来考虑一下
G(n)=∑i=1nd(i)G(n)=∑i=1nd(i)
那么;
G(n)=∑i=1d(i)=∑i=1⌊ni⌋G(n)=∑i=1d(i)=∑i=1⌊ni⌋
然后分块处理:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#define maxx 5000000
#define mod 1000000007
#define ll long long
using namespace std;
map<ll,ll>g;
ll G(ll n)
{
if(g[n])return g[n];
ll ans=0;
//while((ans+1)*(ans+1)<=n)ans++;
for(ll i=1,last;i<=n;i=last+1)
{
last=n/(n/i);
ans+=(last-i+1)*(n/i);
}
g[n]=ans;
return ans;
}
map<ll,ll>f;
ll F(ll n)
{
if(f[n])return f[n];
ll ans=0;
for(ll i=1,last;i<=n;i=last+1)
{
last=n/(n/i);
ans+=(G(last)-G(i-1))*(n/i);
}
f[n]=ans;
return ans;
}
int main()
{
ll a,b;
cin>>a>>b;
cout<<(F(b)-F(a-1)+b-a+1)/2<<endl;
return 0;
}
但是这种推导下来,我能预计会超时,但是居然还wa了,实在不知道为啥,希望有大佬可以指出原因。
换另一种推法:
ans(n)=∑i=1n∑j=1n[ijgcd(i,j)<=n?1:0]ans(n)=∑i=1n∑j=1n[ijgcd(i,j)<=n?1:0]
枚举gcd:
ans(n)=∑d=1n∑d|in∑d|jn[gcd(i,j)==d][ijd<=n]=∑d=1n∑i=1⌊nd⌋∑j=1⌊nd⌋[gcd(i,j)==1][ijd<=n]ans(n)=∑d=1n∑d|in∑d|jn[gcd(i,j)==d][ijd<=n]=∑d=1n∑i=1⌊nd⌋∑j=1⌊nd⌋[gcd(i,j)==1][ijd<=n]
这里可以引入欧拉函数或者莫比乌斯函数,但是欧拉函数似乎没什么用啊。
用莫比乌斯搞一下:
=∑d=1n∑i=1⌊nd⌋∑j=1⌊nd⌋∑k|gcd(i,j)μ(k)[ijd<=n]=∑d=1n∑k=1⌊nd⌋μ(k)∑k|i⌊nd⌋∑k|j⌊nd⌋[ijd<=n]=∑d=1n∑d|knμ(k)∑i=1⌊ndk⌋∑j=1⌊ndk⌋[ijk2d<=n]=∑k=1nμ(k)∑d|k∑i=1⌊ndk⌋∑j=1⌊ndk⌋[ijk2d<=n]=∑k=1nμ(k)∑d=1⌊nk⌋∑i=1⌊ndk⌋∑j=1⌊ndk⌋[ijd<=nk2]=∑d=1n∑i=1⌊nd⌋∑j=1⌊nd⌋∑k|gcd(i,j)μ(k)[ijd<=n]=∑d=1n∑k=1⌊nd⌋μ(k)∑k|i⌊nd⌋∑k|j⌊nd⌋[ijd<=n]=∑d=1n∑d|knμ(k)∑i=1⌊ndk⌋∑j=1⌊ndk⌋[ijk2d<=n]=∑k=1nμ(k)∑d|k∑i=1⌊ndk⌋∑j=1⌊ndk⌋[ijk2d<=n]=∑k=1nμ(k)∑d=1⌊nk⌋∑i=1⌊ndk⌋∑j=1⌊ndk⌋[ijd<=nk2]
根据现在的条件约束,我们是可以适当的缩写范围的。
ans=∑k=1n√μ(k)∑d=1⌊nk2⌋∑i=1⌊ndk2⌋∑j=1⌊ndk2⌋[ijd<=nk2]ans=∑k=1nμ(k)∑d=1⌊nk2⌋∑i=1⌊ndk2⌋∑j=1⌊ndk2⌋[ijd<=nk2]
这里就只要去枚举(i,j,d),因为这三个的地位是相等的,我们假设i<=j=d,那么i就只需要枚举x13x13次
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#define maxx 316238
#define mod 1000000007
#define ll long long
#define LL long long
using namespace std;
bool isP[maxx];
int prime[maxx];
int cnt;
int mu[maxx];
void init()
{
mu[1]=1;
for(int i=2;i<maxx;i++)
{
if(!isP[i])
{
prime[cnt++]=i;
mu[i]=-1;
}
for(int j=0;j<cnt&&(ll)i*prime[j]<maxx;j++)
{
isP[i*prime[j]]=true;
if(i%prime[j])
mu[i*prime[j]]=-mu[i];
else
{
mu[i*prime[j]]=0;
break;
}
}
}
}
ll F(ll x)
{
if(!x)return 0;
ll ans=0;
for(ll k=1;k*k<=x;k++)
{
if(mu[k]!=0)
{
ll m=x/(k*k),s=0;
for(ll i=1;i*i*i<=m;i++)
{
for(int j=i+1;i*j*j<=m;j++)
s+=(m/(i*j)-j)*6+3;
s+=(m/(i*i)-i)*3+1;
}
ans+=mu[k]*s;
}
}
return (ans+x)/2;
}
int main()
{
init();
ll a,b;
cin>>a>>b;
cout<<F(b)-F(a-1)<<endl;
return 0;
}
探讨了在给定区间内寻找具有特定最小公倍数的二元组数量的方法,利用数学技巧和算法优化实现高效计算。
595

被折叠的 条评论
为什么被折叠?



