太阳神

来源:互联网 发布:淘宝代运营公司收费 编辑:程序博客网 时间:2024/05/17 09:41

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

【输入格式】
第一行一个正整数n。
【输出格式】
一行一个整数表示答案,对1,000,000,007取模。

【样例输入输出】
ra.in
3
ra.out
2

【数据范围与约定】
对于20%的数据n<=2000;
对于40%的数据n<=10000000;
对于60%的数据n<=100000000;
对于80%的数据n<=1000000000;
对于100%的数据n<=10000000000

分析:

以下内容是搬运+整理

受“序列计数”的启发,用一个小小的容斥,

用n^2减去lcm(a,b)<=n的对数

我们枚举d=gcd(a,b),令a’=a/d,b’=b/d;

a=a’d,
b=b’d
lcm(a,b)=a’d*b’d/d=a’b’d<=n

即我们要求的是
gcd(a’,b’)=1 //不然gcd(a,b)!=d
且a’b’d<=n的三元组(a’,b’,d)个数
这里写图片描述
反演一下:
这里写图片描述
美化一下
这里写图片描述
这里i是小于等于n^0.5的,因此我们可以线筛出μ函数的值
现在问题在于如何求满足abc<=m的三元组个数
我们可以限定a<=b<=c,先计算满足这个条件的三元组个数
显然a可以在m^(1/3)的范围内枚举,
b可以在(m/a)^0.5的范围内枚举,
c的个数可以直接O(1)计算

总的时间复杂度O(n^(2/3))

这里写代码片#include<cstdio>#include<cstring>#include<iostream>#include<cmath>#define ll long longusing namespace std;const int mod=1000000007;const int N=100005;int n,ans=0;int sshu[N],tot=0,mu[N];bool no[N];void doit(){    int i;    memset(no,0,sizeof(no));    mu[1]=1;    for (i=2;i<=N;i++)    {        if (!no[i])        {            mu[i]=-1;  //素数             sshu[++tot]=i;        }        for (int j=1;j<=tot&&sshu[j]*i<=N;j++)        {            no[i*sshu[j]]=1;             if (i%sshu[j]==0)            {                mu[i*sshu[j]]=0;  //含平方因子                 break;            }            mu[i*sshu[j]]=-mu[i];  //积性函数         }    }    int unit=trunc(sqrt(n));    for (int i=1;i<=unit;i++)    {        int cnt;        if (!mu[i]) continue;        ll x=n/(1ll*i*i);        cnt=0;        for (int a=1;(ll)1ll*a*a*a<=x;a++)        {            int lb=trunc(sqrt(x/a));            cnt-=2; cnt%=mod; cnt+=mod; cnt%=mod;        }        int la=trunc(sqrt(x));        for (int a=1;a<=la;a++)            cnt=(cnt-(x/1ll*a*a)*3%mod)%mod,cnt%=mod,cnt+=mod,cnt%=mod;        ans=(ans+cnt*1ll*(n%mod)*(n%mod)%mod+mod)%mod;    }    ans=(((n%mod)*(n%mod)%mod-ans)%mod+mod)%mod;}int main(){    //freopen("ra.in","r",stdin);      //freopen("ra.out","w",stdout);    scanf("%d",&n);    doit();    printf("%d",ans);    return 0;}
原创粉丝点击