洛谷P2257 YY的GCD【莫比乌斯反演】
题解
和前一道模板题类似,先设 \(f(d)\),再得 \(F(n)\),然后反演 \(f(n)\)。因为最终答案:
\[Ans=\sum_{n\in prime}f(n)=\sum_{n\in prime}\sum_{t=1}^{\big\lfloor\frac{N}{n}\big\rfloor}\mu(t)\bigg\lfloor\frac{N}{nt}\bigg\rfloor\bigg\lfloor\frac{M}{nt}\bigg\rfloor
\]
还可以继续处理,但这里我还有点想不明白,可以先枚举 \(T=nt\):
\[Ans=\sum_{T=1}^{N}\bigg\lfloor\frac{N}{T}\bigg\rfloor\bigg\lfloor\frac{M}{T}\bigg\rfloor\sum_{n|T,n\in prime}\mu(\frac{T}{n})
\]
后面那一坨就可以在求 \(\mu\) 之后处理出来,然后算 \(Ans\),就与前一道模板题差不多了,预处理后面那一坨得前缀和,然后每次询问用整除分块计算。
代码
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
typedef long long LL;
const int N=1e7+10;
int n,m,vis[N],prime[N],cnt,mu[N];
LL sum[N];
void euler(int n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]) {prime[++cnt]=i;mu[i]=-1;}
for(int j=1;1ll*i*prime[j]<=n;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0) {mu[i*prime[j]]=0;break;}
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=cnt;i++)
for(int j=1;1ll*j*prime[i]<=n;j++)
sum[j*prime[i]]+=mu[j];
for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
}
int main(){
euler(1e7);
int T;scanf("%d",&T);
while(T--){
LL ans=0;
scanf("%d%d",&n,&m);
if(n>m) swap(n,m);
for(int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
ans+=1ll*(sum[r]-sum[l-1])*(n/l)*(m/l);
}
printf("%lld\n",ans);
}
return 0;
}