莫比乌斯反演入门题
设f(k)为gcd(x,y)=k的数对(x,y)的对数,我们要求的是f(1)
设F(k)为gcd(x,y)为k的倍数的数对(x,y)的对数,可以想到F(k)=floor(b/k)*floor(d/k),
由莫比乌斯反演得:
令lim=min(b/k,d/k)
f(1)=mu[1]*F(1) + mu[2]*F[2] + ... + mu[lim]*F(lim)
因为(n1,n2)和(n2,n1)算为同一种情况,所以最后结果还要减掉重复的情况。
#include <iostream> #include <algorithm> #include <string.h> #include <cstdio> #include <cmath> using namespace std; const int maxn=1000005; bool check[maxn]; int mu[maxn]; int prime[maxn]; void Moblus() { memset(check,false,sizeof(check)); int tot=0; mu[1]=1; for(int i=2; i<maxn; i++) { if(!check[i]) { prime[tot++]=i; mu[i]=-1; } for(int j=0; j<tot; j++) { if(i * prime[j]>=maxn)break; check[i*prime[j]]=true; mu[i*prime[j]]=-mu[i]; if(i%prime[j] == 0) { mu[i*prime[j]]=0; break; } } } } int main() { Moblus(); int a,b,c,d,k; int cas; scanf("%d",&cas); for(int cc=1; cc<=cas; cc++) { scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); if(k==0) { printf("Case %d: 0 ",cc); continue; } b/=k; d/=k; if(b>d)swap(d,b); long long ans1=0; for(int i=1; i<=b; i++) ans1+=(long long )mu[i]*(b/i)*(d/i); long long ans2=0; for(int i=1; i<=b; i++) ans2+=(long long )mu[i]*(b/i)*(b/i); ans1-=ans2/2; printf("Case %d: %I64d ",cc,ans1); } return 0; }