HDU - 4746 (莫比乌斯反演)

本文介绍了一种算法,用于计算在给定整数P的情况下,两个整数a和b构成的数对(a,b)的数量,其中a和b的最大公约数是P的幸运数。幸运数是指一个数的质因数个数不超过P。文章详细解释了算法原理,并提供了实现代码。

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

As we know, any positive integer C ( C >= 2 ) can be written as the multiply of some prime numbers:
    C = p1×p2× p3× ... × pk
which p1, p2 ... pk are all prime numbers.For example, if C = 24, then:
    24 = 2 × 2 × 2 × 3
    here, p1 = p2 = p3 = 2, p4 = 3, k = 4

Given two integers P and C. if k<=P( k is the number of C's prime factors), we call C a lucky number of P.

Now, XXX needs to count the number of pairs (a, b), which 1<=a<=n , 1<=b<=m, and gcd(a,b) is a lucky number of a given P ( "gcd" means "greatest common divisor").

Please note that we define 1 as lucky number of any non-negative integers because 1 has no prime factor.




Input


The first line of input is an integer Q meaning that there are Q test cases.
Then Q lines follow, each line is a test case and each test case contains three non-negative numbers: n, m and P (n, m, P <= 5×10 5. Q <=5000).



Output


For each test case, print the number of pairs (a, b), which 1<=a<=n , 1<=b<=m, and gcd(a,b) is a lucky number of P.


Sample Input
2
10 10 0
10 10 1
Sample Output
63 
93 


还是老样子,设 f(d) 为 gcd( a, b )=d 的数目,则F( d ) 为 gcd (a , b) 是 d 的倍数的数目,则F(d)就是(n/d)*(m/d)
然后有:(设 miu[ ] 是莫比乌斯函数)
f(1)=miu(1)*F(1)+miu(2)*F(2)+.........+miu(p1*p2*......*pi)*F(p1*p2*.......*pi)
f(2)=miu(1)*F(1*2)+miu(2)*F(2*2)+..........+miu(p1*p2*.....*pj)*F(p1*p2*.......*pj*2)

………………

f(d)=miu(1)*F(1*d)+miu(2)*F(2*d)+..........+miu(p1*p2*.....*pk)*F(p1*p2*.......*pk*d)

然后把 f(i) 中的质因子个数小于等于 p 的 i 对应的 f(i)加起来就得到 ans=f(1)+f(2)+.....+f(i)+........  =  a(1)*F(1)+a(2)*F(2)+.......+a(i)*F(i)+.......
其中a(i)为 i 相同的F(i)对应的各种  miu(i / j)的和。。。。。emmm…… 也就是说,对于一个 j ,筛出他的倍数 i ,则这个 j 对 F(i) 的贡献为 a( i )+=miu(i / j)。。。
然后莫比乌斯函数求和啊分段把 F(i) 加起来啊就完了
(我好没用实在讲不清楚。。。。。。。。)
当然,同时还要注意 gcd 是不是 lucky  number ,也就是限制其质因子个数小于等于 p
这个时候我们可以开一个二维数组 a[ i ][ j ]  表示 i 的倍数中质因子个数至多为 j,用这个数组来存对应的莫比乌斯函数的和
以下 K[ i ] 表示 i 的质因子有多少个,这个题他时间卡的紧(反正我是这么觉得),所以 K 数组不要用传统的那种一个一个遍历素数表的方法打表,会T的
可以跟着素数表直接打出来,这个比较快。
还有,以下 F[ i ][ j ] 即上述的  a[ i ][ j ]

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
const LL MOD=1e9+7;
#define fi first
#define se second
const int MAXN=1000000+23;
LL prime[MAXN],total,miu[MAXN];
bool isprime[MAXN];
LL F[MAXN][23],K[MAXN];
void make()
{
    int m=MAXN-3;
    memset(isprime,true,sizeof(isprime));
    memset(K,0,sizeof(K));
    total=0;
    isprime[0]=isprime[1]=false;
    miu[1]=1;
    for(int i=2;i<=m;i++)
    {
        if(isprime[i]){prime[++total]=i;miu[i]=-1;K[i]=1;}
        for(int j=1;j<=total&&prime[j]*i<=m;j++)
        {
            K[i*prime[j]]=K[i]+1;
            isprime[i*prime[j]]=false;
            if(i%prime[j])
            {
                miu[i*prime[j]]=-miu[i];
            }
            else
            {
                miu[i*prime[j]]=0;
                break;
            }
        }
    }
    miu[0]=0;
//    for(int i=1;i<=m;i++)miu[i]+=miu[i-1];
}

LL n,m,p;

void Init()
{
    LL m=5e5+230,_max=0;
    for(LL i=1;i<=m;i++)
    {
        _max=max(_max,K[i]);
        for(int j=1;j*i<=m;j++)
        {
            F[j*i][K[i]]+=miu[j];
        }
    }
    for(int j=1;j<=m;j++)
    {
        for(int i=1;i<=_max;i++)
        {
            F[j][i]+=F[j][i-1];
        }
    }
    for(int i=0;i<=_max;i++)
    {
        for(int j=2;j<=m;j++)
        {
            F[j][i]+=F[j-1][i];
        }
    }
}

int main()
{
    make();
    Init();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld%lld",&n,&m,&p);
        if(p>18)
        {
            printf("%lld\n",n*m);
            continue;
        }
        if(n>m)swap(n,m);
        LL ans=0;
        for(LL i=1,last=0;i<=n;i=last+1)
        {
            last=min(n/(n/i),m/(m/i));
            ans+=(F[last][p]-F[i-1][p])*((n/i)*(m/i));
        }
        printf("%lld\n",ans);
    }
    return 0;
}
emmmm............我才刚开始莫比乌斯专题,对每个题目的理解度不高,比如这个题目写的博客就和大佬们差距灰常大,所以请各位多多指正在下文意不清或者是有错误的地方
。。。。。。。(怀疑人生中)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值