题目链接
题意
求 ∑x=1n∑y=1m[gcd(x,y)=d]。
多组数据。
思路
新学的莫比乌斯反演,拿这题练练手。
设 f(x)=∑i=1n∑j=1m[gcd(i,j)=x],g(x)=∑i=1n∑j=1m[x∣gcd(i,j)]。
显然 g(x)=∑x∣imin(n,m)f(i)。
由莫比乌斯反演得:f(x)=∑x∣imin(n,m)μ(i)⋅g(xi)。
g(x)=i=1∑nj=1∑m[x∣gcd(i,j)]=i=1∑⌊xn⌋j=1∑⌊xm⌋[1∣gcd(i,j)]=⌊xn⌋⋅⌊xm⌋
答案即为 f(d)。
f(d)=d∣i∑min(n,m)μ(di)⋅g(i)=i=1∑min(n,m)μ(i)⋅g(id)=i=1∑min(n,m)μ(i)⋅⌊idn⌋⋅⌊idm⌋
实现
整除分块即可。
时间复杂度 O(max(n,m)+qmax(n,m))。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include<cstdio> using namespace std; const int N=5e4; int q,n,m,d,cnt,pri[N+1],mu[N+1],smu[N+1]; bool npri[N+1]; long long ans; template<class T>inline T min(T x,T y) { return x<=y?x:y; } void init() { mu[1]=smu[1]=1; for(int i=2;i<=N;++i) { if(!npri[i]) { pri[++cnt]=i; mu[i]=-1; } for(int j=1,tem;(j<=cnt)&&((tem=i*pri[j])<=N);++j) { npri[tem]=true; if(i%pri[j]==0) { break; } mu[tem]=-mu[i]; } smu[i]=smu[i-1]+mu[i]; } return; } int main() { init(); scanf("%d",&q); while(q--) { scanf("%d%d%d",&n,&m,&d); ans=0; for(int l=1,r;(l<=n)&&(l<=m);l=r+1) { r=min(n/(n/l),m/(m/l)); ans+=1ll*(n/l/d)*(m/l/d)*(smu[r]-smu[l-1]); } printf("%lld\n",ans); } return 0; }
|