【题意】给定a和b,求满足a<=lcm(x,y)<=b && x<y的数对(x,y)个数。a,b<=10^11。
【算法】莫比乌斯反演+组合计数
【题解】★具体推导过程参考:51nod1222 最小公倍数计数
过程运用到的技巧:
1.将所有i和j的已知因子提取出来压缩上届。
2.将带有μ(k)的k提到最前面,从而后面变成单纯的三元组形式。
最终形式:
$$ans=sum_{k=1}^{sqrt n} mu(k) sum_{d} sum_{i} sum_{j} [i*j*d<=frac{n}{k^2}]$$
问题转化为枚举(d,i,j)三元组满足其乘积<=n/k^2。
虽然题目要求组合(有序),但是有d的存在,所以先求排列(无序)。
但是三元组的排列不方便统计,所以求三元组的组合乘上排列系数
先算严格从小到大的,然后减去两个相同的和三个相同的。
最后+n后/2就变成组合。
听说复杂度O(n^(2/3))。
#include<cstdio> #include<cmath> #define int long long using namespace std; const int maxn=1000010; int miu[maxn],prime[maxn],tot; bool vis[maxn]; int solve(int x){ if(!x)return 0; int N=(int)sqrt(x)+1,ans=0; for(int k=1;k<=N;k++)if(miu[k]){ int n=x/(k*k); for(int d=1;d*d*d<=n;d++){ for(int i=d+1;i*i*d<=n;i++)ans+=miu[k]*((n/(d*i)-i)*6+3); ans+=miu[k]*((n/(d*d)-d)*3+1); } } return (ans+x)/2; } #undef int int main(){ #define int long long int A,B; scanf("%lld%lld",&A,&B); int N=(int)sqrt(B)+1; miu[1]=1; for(int i=2;i<=N;i++){ if(!vis[i]){miu[prime[++tot]=i]=-1;} for(int j=1;j<=tot&&i*prime[j]<=N;j++){ vis[i*prime[j]]=1;// if(i%prime[j]==0)break; miu[i*prime[j]]=-miu[i]; } } printf("%lld ",solve(B)-solve(A-1)); return 0; }