https://www.luogu.org/problemnew/show/P2257
求 (n,m) 中 (gcd(i,j)==p) 的数对的个数
求 $sumlimits_p sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}[gcd(i,j)==p] $
由套路:
(=sumlimits_p sumlimits_{k=1}^{N}mu(k) lfloorfrac{n}{kp}
floor lfloorfrac{m}{kp}
floor)
再套路:
(=sumlimits_p sumlimits_{T=kp}^{N}mu(frac{T}{p}) lfloorfrac{n}{T}
floor lfloorfrac{m}{T}
floor)
交换求和:
(=sumlimits_{T=1}^{N} sumlimits_{p|T} mu(frac{T}{p}) lfloorfrac{n}{T}
floor lfloorfrac{m}{T}
floor)
提T:
(=sumlimits_{T=1}^{N} lfloorfrac{n}{T}
floor lfloorfrac{m}{T}
floor sumlimits_{p|T} mu(frac{T}{p}))
后面的式子可以预处理,方法是在筛出质数表和莫比乌斯函数表之后,枚举每个质数p,再枚举倍数k,给kp加上 (mu(k)) .
前面的式子可以整除分块 (r=min(n/(n/l),m/(m/l))) .
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 10000000+5
/* 莫比乌斯函数筛 begin */
int mu[MAXN];
int pri[MAXN],pritop;
bool notpri[MAXN];
//pritop从1开始计数
int sumdmu[MAXN],prefixsumdmu[MAXN];
void sieve3(int n) {
notpri[1]=mu[1]=1;
for(int i=2; i<=n; i++) {
if(!notpri[i])
pri[++pritop]=i,mu[i]=-1;
for(int j=1; j<=pritop&&i*pri[j]<=n; j++) {
notpri[i*pri[j]]=1;
//略有不同
if(i%pri[j])
mu[i*pri[j]]=-mu[i];
else {
mu[i*pri[j]]=0;
break;
}
}
}
for(int j=1; j<=pritop; j++) {
for(int i=1;i*pri[j]<=n;i++){
sumdmu[i*pri[j]]+=mu[i];
}
}
for(int i=1;i<=n;i++)
prefixsumdmu[i]=prefixsumdmu[i-1]+sumdmu[i];
}
/* 莫比乌斯函数筛 end */
//整除分块,n,m版
ll aliquot_patition(int n,int m) {
ll ans=0;
int N=min(n,m);
for(int l=1,r; l<=N; l=r+1) {
r=min(n/(n/l),m/(m/l));
ans+=1ll*(n/l)*(m/l)*(prefixsumdmu[r]-prefixsumdmu[l-1]);
}
return ans;
}
int main() {
sieve3(10000000);
int T;
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&n,&m);
printf("%lld
",aliquot_patition(n,m));
}
}