【51nod 1222】 最小公倍数计数

探讨了在给定区间内寻找具有特定最小公倍数的二元组数量的方法,利用数学技巧和算法优化实现高效计算。

**题目来源: 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=1nj=1n[ijgcd(i,j)==n]=d|nd|id|j[ijd==n]=d|nindjnd[ijd==n]=d|nindjnd[ij==nd]=d|ni=1dj=1d[ij==d]=d|nk|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=1nj|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=1niG(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=1nj=1n[ijgcd(i,j)<=n?1:0]ans(n)=∑i=1n∑j=1n[ijgcd(i,j)<=n?1:0]

枚举gcd:
ans(n)=d=1nd|ind|jn[gcd(i,j)==d][ijd<=n]=d=1ni=1ndj=1nd[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=1ni=1ndj=1ndk|gcd(i,j)μ(k)[ijd<=n]=d=1nk=1ndμ(k)k|indk|jnd[ijd<=n]=d=1nd|knμ(k)i=1ndkj=1ndk[ijk2d<=n]=k=1nμ(k)d|ki=1ndkj=1ndk[ijk2d<=n]=k=1nμ(k)d=1nki=1ndkj=1ndk[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=1nk2i=1ndk2j=1ndk2[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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值