SPOJ-PGCD Primes in GCD Table

题目链接: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 }

     

  

“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”
原文地址:https://www.cnblogs.com/Blogggggg/p/8954320.html