[SDOI 2015] 约数个数和(约数个数函数性质 + 莫比乌斯反演) | 错题本

文章目录

题目

[SDOI 2015] 约数个数和

分析

d ( x y ) = ∑ d 1 ∣ x ∑ d 2 ∣ y [ gcd ⁡ ( d 1 , d 2 ) = 1 ] d(xy)=\sum_{d_1|x}\sum_{d_2|y}[\gcd(d_1,d_2) = 1] d(xy)=d1xd2y[gcd(d1,d2)=1] 我们证明每个因子与 gcd ⁡ ( d 1 , d 2 ) = 1 \gcd(d_1,d_2) = 1 gcd(d1,d2)=1 的双射关系:
x = ∏ i = 1 n p i a i , y = ∏ i = 1 n p i b i   ( a i + b i ≠ 0 ) x = \prod_{i = 1}^{n}{p_i}^{a_i}, y = \prod_{i = 1}^{n}{p_i}^{b_i}\ (a_i + b_i \neq 0) x=i=1npiai,y=i=1npibi (ai+bi=0)
考虑 x y xy xy 的一个因子 k = ∏ i = 1 n p i t i k = \prod_{i = 1}^{n}{p_i}^{t_i} k=i=1npiti,如果 t i ≤ a i t_i \leq a_i tiai,将 q t i q^{t_i} qti 乘入 d 1 d_1 d1,否则将 q t i − a i q^{t_i - a_i} qtiai 乘入 d 2 d_2 d2,这样一来显然 gcd ⁡ ( d 1 , d 2 ) = 1 \gcd(d_1,d_2) = 1 gcd(d1,d2)=1,也就建立了 k k k gcd ⁡ ( d 1 , d 2 ) = 1 \gcd(d_1,d_2) = 1 gcd(d1,d2)=1 的单射。再考虑由 gcd ⁡ ( d 1 , d 2 ) = 1 \gcd(d_1,d_2) = 1 gcd(d1,d2)=1 映射回 k k k,也很简单,看每个 p i p_i pi d 1 d_1 d1 里面还是在 d 2 d_2 d2 里面即可,这也是一个单射。
由此我们证明出了每个因子与每个 gcd ⁡ ( d 1 , d 2 ) = 1 \gcd(d_1,d_2) = 1 gcd(d1,d2)=1 之间是一一映射的,原式得证。


接下来就是反演了,不妨设 n ≥ m n \geq m nm ∑ i = 1 n ∑ j = 1 m d ( i j ) = ∑ i = 1 n ∑ j = 1 m ∑ d 1 ∣ i ∑ d 2 ∣ j [ gcd ⁡ ( d 1 , d 2 ) = 1 ] = ∑ d 1 = 1 n ∑ d 2 = 1 m ⌊ n d 1 ⌋ ⌊ m d 2 ⌋ [ gcd ⁡ ( d 1 , d 2 ) = 1 ] = ∑ d = 1 m μ ( d ) ∑ d 1 = 1 n ∑ d 2 = 1 m [ d ∣ d 1 ] [ d ∣ d 2 ] ⌊ n d 1 ⌋ ⌊ m d 2 ⌋ = ∑ d = 1 m μ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ⌊ n d ⋅ i ⌋ ⌊ m d ⋅ j ⌋ = ∑ d = 1 m μ ( d ) ( ∑ i = 1 ⌊ n d ⌋ ⌊ ⌊ n d ⌋ i ⌋ ) ( ∑ j = 1 ⌊ m d ⌋ ⌊ ⌊ m d ⌋ j ⌋ ) \begin{aligned}&\sum_{i = 1}^n\sum_{j = 1}^m d(ij) \\=&\sum_{i = 1}^n\sum_{j = 1}^m\sum_{d_1|i}\sum_{d_2|j}[\gcd(d_1,d_2) = 1] \\=&\sum_{d_1 = 1}^n\sum_{d_2 = 1}^m\left\lfloor\frac{n}{d_1}\right\rfloor\left\lfloor\frac{m}{d_2}\right\rfloor[\gcd(d_1,d_2) = 1] \\=&\sum_{d = 1}^m\mu(d)\sum_{d_1 = 1}^n\sum_{d_2 = 1}^m[d|d_1][d|d_2]\left\lfloor\frac{n}{d_1}\right\rfloor\left\lfloor\frac{m}{d_2}\right\rfloor\\=&\sum_{d = 1}^m\mu(d)\sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor}\sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor}\left\lfloor\frac{n}{d\cdot i}\right\rfloor\left\lfloor\frac{m}{d\cdot j}\right\rfloor\\=&\sum_{d = 1}^m\mu(d)\left(\sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor}\left\lfloor\frac{\left\lfloor\frac{n}{d}\right\rfloor}{i}\right\rfloor\right)\left(\sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor}\left\lfloor\frac{\left\lfloor\frac{m}{d}\right\rfloor}{j}\right\rfloor\right)\end{aligned} =====i=1nj=1md(ij)i=1nj=1md1id2j[gcd(d1,d2)=1]d1=1nd2=1md1nd2m[gcd(d1,d2)=1]d=1mμ(d)d1=1nd2=1m[dd1][dd2]d1nd2md=1mμ(d)i=1dnj=1dmdindjmd=1mμ(d)i=1dnidnj=1dmjdm f ( x ) = ∑ i = 1 x ⌊ x i ⌋ f(x) = \sum\limits_{i = 1}^{x}\left\lfloor\frac{x}{i}\right\rfloor f(x)=i=1xix,它可以 O ( n n ) O(n\sqrt n) O(nn ) 初始化,那么原式也可以 O ( n ) O(\sqrt n) O(n ) 求了。

代码

#include <bits/stdc++.h>

typedef long long LL;

const int MAXN = 50000;

LL Num[MAXN + 5];

int Mu[MAXN + 5];
bool Vis[MAXN + 5];
std::vector<int> Primes;

void Init(int n) {
	Mu[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (!Vis[i])
			Mu[i] = -1, Primes.push_back(i);
		for (int j = 0; j < (int)Primes.size() && (LL)i * Primes[j] <= n; j++) {
			Vis[i * Primes[j]] = true;
			if (i % Primes[j] == 0) {
				Mu[i * Primes[j]] = 0;
				break;
			}
			Mu[i * Primes[j]] = -Mu[i];
		}
        Mu[i] = Mu[i - 1] + Mu[i];
	}
	for (int i = 1; i <= n; i++) {
        for (int lft = 1, rgt = 1; lft <= i; lft = rgt + 1) {
            rgt = i / (i / lft);
            Num[i] += (LL)(rgt - lft + 1) * (i / lft);
        }
	}
}

int main() {
    Init(MAXN);
    int T; scanf("%d", &T);
    while (T--) {
        int N, M; scanf("%d%d", &N, &M);
        if (N < M) std::swap(N, M);
        LL Ans = 0;
        for (int lft = 1, rgt = 1; lft <= M; lft = rgt + 1) {
            rgt = std::min(M / (M / lft), N / (N / lft));
            Ans += (LL)(Mu[rgt] - Mu[lft - 1]) * Num[N / lft] * Num[M / lft];
        }
        printf("%lld\n", Ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值