【BZOJ2818】Gcd(莫比乌斯反演)

本文介绍如何使用莫比乌斯反演解决组合数学问题,通过实例详细解析了求解gcd(x,y)==k的个数的方法,并给出了C++实现代码。

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

记录一个菜逼的成长。。

题目链接

莫比乌斯反演是组合数学中很重要的内容,可以用于解决很多组合数学的问题。
参考
假设有两个定义在非负整数集上的函数 f(n) F(n)
有两种表述形式
第一种:

F(n)=d|nf(d)

f(n)=d|nμ(d)F(nd)

第二种:
F(n)=n|df(d)

f(n)=n|dμ(dn)F(d)

关于莫比乌斯函数 μ(d) 的定义,如下:
1.若 d=1 ,则 μ(d)=1
2.若 d=p1p2,...,pk , pi 均为互异素数,则 μ(d)=(1)k
3.其他情况下 μ(d)=0

这里用到第二种表述
我们假设
f(k) 等于gcd(x,y) == k的个数
F(k)等于gcd(x,y) == k的倍数的个数

因为 gcd(x,y)==k 等价于 gcd(xk,yk)==1
所以我们可以把范围缩小到(1,N/k)
也就是说我们要求的f(k) 变成了f(1)

根据上述第二种表述,令n==1,即
f(1)=Nkd=1μ(d)F(d)

根据F(d)的假设,有 F(d)=N/kdM/kd 共有这么多 gcd(x,y)==dk 的个数
(N为x的区间,M为y的区间,在这题中 N==M )

所以 f(1)=Nkd=1μ(d)N/kdM/kd

然后我们枚举每一个k,在这题中是[1,N]的素数
然后求出每一个k对应的 f(1) ,将这些 f(1) 全部相加即可

跑了4s多。

#include <bits/stdc++.h>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
typedef long long LL;
const int maxn = 10000000 + 10;
bool check[maxn];
int mu[maxn],prime[maxn];
void Mobius()
{
    cl(check,false);
    mu[1] = 1;
    int tot = 0;
    for( int i = 2; i < maxn; i++ ){
        if(!check[i]){
            prime[tot++] = i;
            mu[i] = -1;
        }
        for( int j = 0; j < tot; j++ ){
            if(i * prime[j] >= maxn)break;
            check[i*prime[j]] = true;
            if(i % prime[j] == 0){
                mu[i * prime[j]] = 0;
                break;
            }
            else {
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
}
int main()
{
    Mobius();
    int n;
    while(~scanf("%d",&n)){
        LL ans = 0;
        for( int i = 0; prime[i] <= n; i++ ){
            int tmp = n / prime[i];
            for( int j = 1; j <= tmp; j++ ){
                ans += (LL)mu[j] * (tmp/j) * (tmp/j);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值