acwing 1358. 约数个数和(莫比乌斯函数)

本文介绍了一种计算整数N和M的每个整数i的约数个数d(i)之积的算法,基于数论中的因子分解和欧拉函数,使用C++实现。

设 d(x)�(�) 为 x� 的约数个数,给定 N,M�,�,求

∑i=1N∑j=1Md(ij)∑�=1�∑�=1��(��)

输入格式

输入多组测试数据。

第一行,一个整数 T�,表示测试数据的组数。

接下来的 T� 行,每行两个整数 N、M�、�。

输出格式

T� 行,每行一个整数,表示你所求的答案。

数据范围

1≤N,M,T≤500001≤�,�,�≤50000

输入样例:
2
7 4
5 6
输出样例:
110
121

 思路:

推导比较麻烦;

 代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const double eps = 1e-8;
const int N = 50000+100;
#define LL long long 
int pre[N], mu[N],st[N],h[N];
int n,cn,m;
long long res;
int g(int l, int k)
{
    if (k / l == 0) return n;
    return k / (k / l);
}
void into()
{
    mu[1] = 1;
    for (int i = 2; i <= N; i++)
    {
        if (!st[i]) pre[++cn] = i, mu[i] = -1;
        for (int j = 1; pre[j] * i <= N&&j<=cn; j++)
        {
            st[pre[j] * i] = 1;
            if (i % pre[j] == 0) break;
            mu[i*pre[j]] = -mu[i];
        }
    }
    for (int i = 1; i <= N; i++)
        mu[i] += mu[i - 1];
    for(int i=1;i<=N;i++)
    {
        for (int l = 1,r;l <= i; l = r + 1)
        {
            r = min(i, g(l, i));
            h[i] += (r - l + 1) * (i / l);
        }
    }
}
long long f(int a, int b)
{
    res = 0;
    n = min(a, b);
    for (int l = 1,r; l <=n; l=r+1)
    {
        r = min(n, min(g(l, a), g(l, b)));
        res += (long long )(mu[r] - mu[l - 1]) * h[a / l] * h[b / l];
    }
    return res;
}
int main()
{
    into();
    int T;
    cin >> T;
    while (T--)
    {
        cin >> n >> m;
        cout << f(n, m) << endl;
    }
    return 0;
}

### 使用莫比乌斯函数计算约数 莫比乌斯反演是一种重要的工具,在数论领域被广泛用于解决涉及整除关系的问题。通过莫比乌斯函数 \( \mu(n) \)[^1] 其性质,可以有效地计算某些复杂的数论函数,比如约数。 #### 莫比乌斯函数定义回顾 莫比乌斯函数 \( \mu(n) \) 的定义如下: - 当 \( n = 1 \),\( \mu(1) = 1 \); - 当 \( n \) 是 \( k \) 个不同质数的乘积时,\( \mu(n) = (-1)^k \); - 否则,\( \mu(n) = 0 \). 这一特性使得它成为处理整除问题的强大工具[^2]。 #### 计算约数的核心理论 设 \( f(n) \) 表示某个正整数 \( n \) 的某种属性(例如约数),而 \( g(n) \) 则表示该属性的一个累积版本。如果满足以下条件: \[ g(n) = \sum_{d|n} f(d), \] 那么可以通过莫比乌斯反演得到 \( f(n) \): \[ f(n) = \sum_{d|n} \mu\left(\frac{n}{d}\right) g(d). \][^3] 对于具体的约数问题,假设我们希望求解某区间内的所有数的约数,则可以根据上述公式设计算法来高效完成此任务。 #### 实现代码示例 下面是一个基于线性筛法实现莫比乌斯函数并利用其计算约数的例子: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e6 + 5; int mu[MAXN], prime_cnt, primes[MAXN]; bool not_prime[MAXN]; void sieve(int max_val){ memset(not_prime, false, sizeof(bool)*(max_val+1)); mu[1] = 1; for (int i = 2; i <= max_val; ++i){ if (!not_prime[i]){ primes[++prime_cnt] = i; mu[i] = -1; } for (int j = 1; j <= prime_cnt && i * primes[j] <= max_val; ++j){ not_prime[i*primes[j]] = true; if (i % primes[j] == 0){ mu[i*primes[j]] = 0; break; } else{ mu[i*primes[j]] = -mu[i]; } } } } long long divisor_sum(int N){ vector<long long> prefix(N+1, 0); for(int d=1; d<=N; ++d){ for(int m=d; m<=N; m+=d){ prefix[m] += d; } } long long result = 0; for(int n=1; n<=N; ++n){ result += mu[n]*prefix[N/n]; } return abs(result); } int main(){ int N; cin >> N; sieve(N); cout << divisor_sum(N) << endl; } ``` 以上程序首先初始化了一个范围内的莫比乌斯函数值表,并随后应用这些预计算的结果去快速得出给定范围内所有自然数的约数[^4]。 ### 注意事项 实际操作过程中需要注意数据类型的选取以及边界条件的设定,以防止溢出或者错误判断等问题的发生。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值