题目描述
太阳神拉很喜欢最小公倍数,有一天他想到了一个关于最小公倍数的题目。
求满足如下条件的数对$(a,b)$对数:$a,b$均为正整数且$a,b\leqslant n$而$lcm(a,b)>n$。其中的$lcm$当然表示最小公倍数。答案对$1,000,000,007$取模
输入格式
第一行一个正整数$n$。
输出格式
一行一个整数表示答案,对$1,000,000,007$取模。
样例
样例输入:
3
样例输出:
2
数据范围与提示
对于$20\%$的数据$n\leqslant 2,000$;
对于$40\%$的数据$n\leqslant 10,000,000$;
对于$60\%$的数据$n\leqslant 100,000,000$;
对于$80\%$的数据$n\leqslant 1,000,000,000$;
对于$100\%$的数据$n\leqslant 10,000,000,000$。
题解
一般遇到这种题,我们都是先将其化繁,再化简。
就是我们现有一个简,但是时间复杂度高的算法;然后将其化为一个繁,时间复杂度依然高的算法;再将这个算法化成一个既简单时间复杂度又低的算法。
那么我们先将其化繁,哦,不,先化简。
发现直接求$lcm(a,b)>n$很不好求,不妨求其补集,也就是$lca(a,b)<=n$。
开始化繁……
我们知道,$lcm(a,b)=\dfrac{a\times b}{gcd(a,b)}$,所以式子可以转化为:
$\sum \limits_{i=1}^n\sum \limits_{j=1}^n\dfrac{i\times j}{gcd(i,j)}\leqslant n$
将$gcd(i,j)$乘过去:
$=\sum \limits_{i=1}^n\sum \limits_{j=1}^ni\times j\leqslant gcd(i,j)\times n$
发现,我们可以枚举$gcd$,也就是:
$=\sum \limits_{g=1}^n\sum \limits_{i=1}^{\left \lfloor \dfrac{n}{g}\right \rfloor}\sum \limits_{j=1}^{\left \lfloor \dfrac{n}{g}\right \rfloor}i\times j\times g^2\leqslant g\times n\times [gcd(i,j)==1]$
约去一个$g$:
$=\sum \limits_{g=1}^n\sum \limits_{i=1}^{\left \lfloor \dfrac{n}{g}\right \rfloor}\sum \limits_{j=1}^{\left \lfloor \dfrac{n}{g}\right \rfloor}i\times j\times g\leqslant n\times [gcd(i,j)==1]$
遇到长这样的$gcd$我们一般考虑莫比乌斯反演。
那么我们又将其化成了:
$\sum\limits_{g=1}^n\sum\limits_{d=1}^{\left\lfloor\dfrac{n}{g}\right\rfloor}\mu (d)\sum\limits_{i=1}^{\left\lfloor\dfrac{n}{dg}\right\rfloor}\sum\limits_{j=1}^{\left\lfloor\dfrac{n}{dg}\right\rfloor}i\times j\times g\times d^2\leqslant n$
因为我们再枚举$\left\lfloor\dfrac{n}{dg}\right\rfloor$以上是没有用的,所以我们可以将其变成:
$\sum \limits_{d=1}^{\sqrt{n}}\mu(d)\sum \limits_{g=1}^n\sum \limits_{i=1}^n\sum \limits_{j=1}^n i\times j\times g\leqslant \dfrac{n}{d^2}$
那么,我们可以设$i<j<g$,但是需要注意得数需要乘$6$;再设$i,j,g$中有两个相等,那么得数要乘$3$就好啦。
简略证明一下时间复杂度(不知道对不对):
我们在调用函数的时候最外层循环是$i\times i\times i\leqslant n$,内层循环是$i\times j\times j\leqslant n$,在平面直角坐标系上积分可以得到函数的时间复杂度是$\Theta({\frac{n}{i^2}}^{\frac{2}{3}})$,在考虑外面的$\Theta(n^{\frac{1}{2}})$即可得到总的时间复杂度是$\Theta(n^{\frac{2}{3}})$。
时间复杂度:$\Theta(n^{\frac{2}{3}})$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h> using namespace std; const int mod=1000000007; long long n; int pri[100001],mu[100001],cnt; bool v[100001]; long long ans; void pre_work() { mu[1]=1; for(long long i=2;i<=100000;i++) { if(!v[i])mu[pri[++cnt]=i]=-1; for(int j=1;j<=cnt&&i*pri[j]<=100000;j++) { v[i*pri[j]]=1; if(i%pri[j])mu[i*pri[j]]=-mu[i]; else{mu[i*pri[j]]=0;break;} } } } long long get(long long x) { long long res=0; for(long long i=1;i*i*i<=x;i++) { if(i*i*i<=x)res++; for(long long j=i+1;i*j*j<x;j++) res=(res+6*(x/i/j-j)%mod)%mod; } for(long long i=1;i*i<=x;i++) res=(res+3*(x/i/i-(x/i/i>=i))%mod)%mod; return res; } int main() { pre_work(); scanf("%lld",&n); for(long long i=1;i*i<=n;i++) if(mu[i])ans=(ans+mu[i]*get(n/i/i)%mod)%mod; n%=mod; printf("%lld",(n*n-ans+mod)%mod); return 0; }
rp++
原文地址:https://www.cnblogs.com/wzc521/p/11621299.html