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\n",ans);
48     }
49     return 0;
50 }

 

     

  

 

posted @ 2018-04-26 22:45  Blogggggg  阅读(385)  评论(2编辑  收藏  举报