题目链接:https://vjudge.net/problem/SPOJ-PGCD
题目大意:
给定 (N) 和 (M),求满足 ((1 le x le N), (1 le y le M)),且 (gcd(x,y)) 为素数的 ((x,y)) 的对数。
知识点: 莫比乌斯反演
解题思路:
设 (g(p)) 表示满足 ((1 le x le N), (1 le y le M)),且 (gcd(x,y) = p) 的 ((x,y)) 的对数。直接求 (g(p)) 是不可行的,其时间复杂度为 (O(NM)).
再设 (f(p)) 表示满足 ((1 le x le N), (1 le y le M)),且 (p|gcd(x,y)) 的 ((x,y)) 的对数,易知 (f(p)=(N/p)*(M/p))。且不难推出 (f(n) = sum limits_{n|d} g(d)). ((1))
莫比乌斯反演公式:若满足 (F(n) = sum limits_{n|d} f(d)),则有 (f(n) = sum limits_{n|d} mu(frac{d}{n})F(d)).
将其代入式 ((1)) 得:
(g(n) = sum limits_{n|d} mu(frac{d}{n})f(d) = sum limits_{n|d} mu(frac{d}{n})(N/d)(M/d)) ((2))
最终的答案为((p) 代表质数):
(sum limits_{p} g(p) = sum limits_{p} sum limits_{p|d} mu(frac{d}{p})(N/d)(M/d))
(= sum limits_{d}^{min(M,N)} (N/d)(M/d) sum limits_{p|d} mu(frac{d}{p})) ((3))
第二个求和函数可以预处理,枚举每一个质数,更新对应的前缀和。
AC代码:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 typedef long long LL; 5 const int maxn = 1e7+5; 6 bool check[maxn]; 7 int prime[maxn],Mobius[maxn]; 8 int tot; 9 int u[maxn]; 10 11 void init(){ 12 Mobius[1]=1; 13 tot=0; 14 for(int i=2;i<maxn;i++){ 15 if(!check[i]){ 16 prime[tot++]=i; 17 Mobius[i]=-1; 18 } 19 for(int j=0;j<tot&&i*prime[j]<maxn;j++){ 20 check[i*prime[j]]=true; 21 if(i%prime[j]==0){ 22 Mobius[i*prime[j]]=0; 23 break; 24 } else 25 Mobius[i*prime[j]]=-Mobius[i]; 26 } 27 } 28 for(int i=0;i<tot;i++){ 29 for(int j=prime[i];j<maxn;j+=prime[i]){ 30 u[j]+=Mobius[j/prime[i]]; // u[j] 代表分子为 j(对应式3中的d) 的和函数的值 31 } 32 } 33 for(int i=1;i<maxn;i++) u[i]+=u[i-1]; //处理出前缀和 34 } 35 int main(){ 36 init(); 37 int t,n,m; 38 scanf("%d",&t); 39 while(t--){ 40 LL ans=0; 41 int last; 42 scanf("%d%d",&n,&m); 43 for(int i=1;i<=min(n,m);i=last+1){ 44 last=min(n/(n/i),m/(m/i)); // [i,last] 这一段的 u[] 对应 (N/d)(M/d) 相等,不难用实验验证 45 ans+=(LL)(n/i)*(m/i)*(u[last]-u[i-1]); 46 } 47 printf("%lld ",ans); 48 } 49 return 0; 50 }