51nod 1192 Gcd表中的质数

本文介绍了一种计算特定范围内质数数量的高效算法。通过枚举质数并使用莫比乌斯函数进行优化,该算法能在较短时间内计算出M*N表格中质数的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有一个M * N的表格,行与列分别是1 - M和1 - N,格子中间写着行与列的最大公约数Gcd(i, j)(1 <= i <= M, 1 <= j <= N)。
例如:M = 5, n = 4。

1 2 3 4 5
1 1 1 1 1 1
2 1 2 1 2 1
3 1 1 3 1 1
4 1 2 1 4 1

给出M和N,求这张表中有多少个质数。
Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 1000)
第2 - T + 1行:每行2个数M,N,中间用空格分隔,表示表格的宽和高。(1 <= M, N <= 5 * 10^6)
Output
共T行,每行1个数,表示表格中质数的数量。
Input示例
2
10 10
100 100
Output示例
30
2791


分析:

容易想到是枚举质数p,然后推导公式。然而就是这么做。

推导:

默认n<=m
[s]表示如果s为真,则表达是为1,[p]表示p为质数是为1,否则为0;

ANS(m,n)=pi=1npj=1mp[(i,j)=1][p]=pi=1npj=1mpk=1μ(k)[k|i,k|j][p]=pk=1npi=1nkpj=1mkpμ(k)[p]=k=1np=1nknkpmkpμ(k)[p]=k=1np|knkmkμ(kp)[p]=k=1nnkmkp|kμ(kp)[p]=k=1nnkmkf(k)

f(k)可以打表,分析一下f的一些性质:
k=si=1pkii
ki中一个大于2或者有两个大于1,那么f(k)=0;
理解起来也比较直观。接着就可以递推。


/*************************************************************************
    > File Name: 1192.cpp
    > Author: kelvin
    > Mail: 444051232@qq.com 
    > Created Time: 2016年05月18日 星期三 10时49分45秒
 ************************************************************************/

#include<bits/stdc++.h>
using namespace std;
#define REP(i,a,b)  for(int i=a;i<b;++i)
#define LL      long long
#define mset(a,b)   memset(a,b,sizeof a)
#define scan(n) scanf("%d",&n)
const int maxn = 5000006;
const int mod = 1000000007;

int prime[maxn];
LL f[maxn];
int mu[maxn];
void getP()
{
    mu[1]=1;
    REP(i,2,maxn){
        if(!prime[i]){
            prime[++prime[0]]=i;
            mu[i]=-1;   
            f[i]=1;
        }
        for(int j=1;j<=prime[0] && (LL)prime[j]*i<maxn;++j){
            prime[i*prime[j]]=1;
            if(i%prime[j])
            {
                f[i*prime[j]]=mu[i]-f[i];
                mu[i*prime[j]]=-mu[i];
            }
            else{
                f[i*prime[j]]=mu[i];
                 break;
            }
        }
    }
    REP(i,2,maxn)   f[i]+=f[i-1];
}


LL getans(int n,int m)
{
    LL ret=0;
    for(int d=1,j;d<=n;d=j+1){
        j=min(m/(m/d),n/(n/d));
        ret+=(LL)(n/d)*(m/d)*(f[j]-f[d-1]);
    }
    return ret;
}

int main()
{
    getP();
    int t;
    scan(t);
    int m,n;
    while(t--){
        scanf("%d%d",&m,&n);
        if(n>m) swap(n,m);
        LL ans=getans(n,m);
        cout<<ans<<endl;
    }


    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值