题目大意
求出从 l 至 r 中满足以下条件的 (x,y) 的个数。
- gcd(x,y)=1 且 gcd(x,y)=x 且 gcd(x,y)=y。
其中 1≤l≤r≤106。
思路
正难则反,所以可以求出所有互质或者是相互倍数的 (x,y) 的对数,在将其减去所有的方案数就是答案。
设 si 表示满足 gcd(a,b)=i 而且 l≤a,b≤r 的数对 (a,b) 的个数。
因为 gcd(a,b)=i,所有有 a,b≥i,而且有 i∣a,i∣b。
假设满足 x∈[l,r] 且 i∣x 的数量为 num,那么以 i 为公因数的个数就是 num×(num−1)。
在以上的计算中 j≥i,且两数满足 i∣a,i∣b,j∣a,j∣b 的情况在计算 si 和 sj 的时候都会计算,所以求出的公因数而不是最小公因数。为了求出 i 为最大公因数时数对的个数,si 的计算方法应该向下面这样。
Si=(⌊r/i⌋−⌈l/i⌉)⋅(⌊r/i⌋−⌈l/i⌉−1)−j=⌈l/i⌉∑⌊r/i⌋Sj×i
其中 s1 表示 (x,y) 的 gcd(x,y)=1 的数量,即 x,y 互质的数量。
对于是倍数的情况,枚举每一个数 i,求出 j∈[l,r] 且 i∣j 的所有的可能的 j 的数量,将答案减去就可以了。
因为如果 x∣y,那么在 x=1,y=1 的情况下,gcd(x,y)=x 就不会与互质的情况重复计算。
时间复杂度为:
O(1r+2r+3r+⋯+r−2r+r−1r+rr)=O(rlogr)
AC Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+6;
int l,r,ans,s[N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>l>>r;
for(int i=r;i>=1;i--){
int cnt=0,sum=0;
for(int j=i;j<=r;j+=i){
sum+=s[j];
if(j>=l){
cnt++;
}
}
s[i]=cnt*(cnt-1)/2-sum;
}
int cnt=0;
for(int i=max(l,2ll);i<=r;i++){
for(int j=2*i;j<=r;j+=i){
cnt++;
}
}
int n=r-l+1;
int ans=n*(n-1)/2;
cout<<(ans-(cnt+s[1]))*2;
return 0;
}