洛谷P3455 [POI2007]ZAP-Queries (莫比乌斯反演)
题意:求$\sum_{i=1}^{a}\sum_{j=1}^{b}[gcd(i,j)==d]$(1<=a,b,d<=50000)。
很套路的莫比乌斯反演。
$\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==k]=\sum_{i=1}^{\lfloor \frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor \frac{m}{k}\rfloor}[gcd(i,j)==1]$
令f(n)为gcd是n的个数,g(n)为gcd是n或n的倍数的个数。
根据反演公式可以得到:$f(n)=\sum_{n|d}\mu(\frac{d}{n})g(d)$
答案即为f(1),对于g函数可以O(1)得到答案,$g(d)=\lfloor \frac{n}{d}\rfloor\lfloor \frac{m}{d}\rfloor$
原式$=\sum_{d=1}^{\lfloor \frac{n}{k}\rfloor}\mu(d){\lfloor \frac{n}{kd}\rfloor}{\lfloor \frac{m}{kd}\rfloor}$
预处理莫比乌斯函数前缀和,后面部分整除分块就行了。
#include <bits/stdc++.h> #define ll long long using namespace std; const int N=5e4+5; int pri[N],tot,mu[N]; bool p[N]; void init() { mu[1]=1; for(int i=2;i<N;i++) { if(!p[i]) mu[i]=-1,pri[tot++]=i; for(int j=0;j<tot&&pri[j]*i<N;j++) { p[i*pri[j]]=true; if(i%pri[j]==0) { mu[i*pri[j]]=0; break; } else mu[i*pri[j]]=-mu[i]; } } for(int i=1;i<N;i++) mu[i]+=mu[i-1]; } int main() { init(); int T,n,m,d; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&d),n/=d,m/=d; if(n>m) swap(n,m); ll ans=0; for(int l=1,r;l<=n;l=r+1) { r=min(n/(n/l),m/(m/l)); ans+=1LL*(mu[r]-mu[l-1])*(n/l)*(m/l); } printf("%lld\n",ans); } return 0; }