【SPOJ PGCD】Primes in GCD Table——莫比乌斯反演2

19 篇文章 0 订阅
3 篇文章 0 订阅

PGCD - Primes in GCD Table

Johnny has created a table which encodes the results of some operation – a function of two arguments. But instead of a boring multiplication table of the sort you learn by heart at prep-school, he has created a GCD (greatest common divisor) table! So he now has a table (of height a and width b), indexed from (1,1) to (a,b), and with the value of field (i,j) equal to gcd(i,j). He wants to know how many times he has used prime numbers when writing the table.

Input

First, t ≤ 10, the number of test cases. Each test case consists of two integers, 1 ≤ a,b < 107.

Output

For each test case write one number - the number of prime numbers Johnny wrote in that test case.

Example

Input:

2
10 10
100 100

Output:

30
2791

在之前已经做了一个莫比乌斯反演的题了,而题所要求得是GCD(x,y) = k的数量,而现在我们所要求得是GCD(x,y) = 质数的数量,和之前一样我们令 f(n)=[gcd(i,j)=n]g(n)=d|nf(d) ,那么我们可以得到 f(n)=n|dμ(dn)g(d)=n|dμ(dn)[xd][yd] ,由于我们是计算gcd(i,j) = 质数的数量,最简单的方式就是枚举质数计算总和,但是可能会超时,注意,对于同一个数d可能会重复计算几次,那么我们可以提前处理出来他被计算的系数,求一个前缀和。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <string> 
#include <algorithm>

using namespace std;

typedef long long LL;

const int Max = 1e7+100;

int mu[Max];

int prime[Max],cnt;

int sum[Max];

bool vis[Max];
// 提前处理

void GetMobius(){

    memset(vis,false,sizeof(vis));

    memset(sum,0,sizeof(sum));

    cnt =  0; 

    mu[1] = 1; 

    sum[0] = 0;

    for(int i = 2;i < Max ; i++){

        if(!vis[i]) {

            prime[cnt++] = i;

            mu[i] = -1;
        }

        for(int j = 0 ; j < cnt && i*prime[j] < Max ;j++){

            vis[i*prime[j]] = true;

            if(i%prime[j]) mu[i*prime[j]] = -mu[i];

            else {

                mu[i*prime[j]] = 0;

                break;
            }
        }
    }

    for(int i = 0;i<cnt ;i++) {

        for(int j = prime[i] ; j < Max ; j+=prime[i]) {

            sum[j]  += mu[j/prime[i]];
        }
    } 

    for(int i = 1;i<Max ;i++) {

        sum[i] += sum[i-1];

    }
} 

LL Cal(LL n,LL  m) {

    int end; LL Sum = 0;

    int S = min(n,m);

    for(int i = 1 ;i <= S ; i= end+1) {

        end = min(n/(n/i),m/(m/i));

        Sum+=((sum[end]-sum[i-1])*(n/i)*(m/i));
    }

    return Sum;
}

int main() {

    int T;

    int n,m;

    LL Sum ;

    GetMobius();

    scanf("%d",&T);

    while(T--) {

        scanf("%d %d",&n,&m);

        Sum = 0;

        Sum += Cal(n,m); 

        printf("%lld\n",Sum);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值