【ybt金牌导航8-7-1】数对统计 / 关于莫比乌斯函数的少量内容

数对统计

题目链接:ybt金牌导航8-7-1

题目大意

给你 n,m,求 gcd(x,y)=1 的数对个数。
1<=x<=n,1<=y<=m

思路

莫比乌斯函数

什么东西

首先我们要知道莫比乌斯函数是个什么鬼东西。

首先,我们先不管莫比乌斯函数,先来看一个函数: F ( n ) = ∑ d ∣ n f ( d ) F(n)=\sum_{d|n}f(d) F(n)=dnf(d)
(假设 f ( d ) f(d) f(d) 是一个给出的函数)

那根据定义,我们可以先弄一下:
F ( 1 ) = f ( 1 ) F(1)=f(1) F(1)=f(1)
F ( 2 ) = f ( 1 ) + f ( 2 ) F(2)=f(1)+f(2) F(2)=f(1)+f(2)
F ( 3 ) = f ( 1 ) + f ( 3 ) F(3)=f(1)+f(3) F(3)=f(1)+f(3)
F ( 4 ) = f ( 1 ) + f ( 2 ) + f ( 4 ) F(4)=f(1)+f(2)+f(4) F(4)=f(1)+f(2)+f(4)
F ( 5 ) = f ( 1 ) + f ( 5 ) F(5)=f(1)+f(5) F(5)=f(1)+f(5)
F ( 6 ) = f ( 1 ) + f ( 2 ) + f ( 3 ) + f ( 6 ) F(6)=f(1)+f(2)+f(3)+f(6) F(6)=f(1)+f(2)+f(3)+f(6)
F ( 7 ) = f ( 1 ) + f ( 7 ) F(7)=f(1)+f(7) F(7)=f(1)+f(7)
F ( 8 ) = f ( 1 ) + f ( 2 ) + f ( 4 ) + f ( 8 ) F(8)=f(1)+f(2)+f(4)+f(8) F(8)=f(1)+f(2)+f(4)+f(8)

那我们考虑用 F ( n ) F(n) F(n) 来推 f ( n ) f(n) f(n)
f ( 1 ) = F ( 1 ) f(1)=F(1) f(1)=F(1)
f ( 2 ) = F ( 2 ) − F ( 1 ) f(2)=F(2)-F(1) f(2)=F(2)F(1)
f ( 3 ) = F ( 3 ) − F ( 1 ) f(3)=F(3)-F(1) f(3)=F(3)F(1)
f ( 4 ) = F ( 4 ) − F ( 2 ) f(4)=F(4)-F(2) f(4)=F(4)F(2)
f ( 5 ) = F ( 5 ) − F ( 1 ) f(5)=F(5)-F(1) f(5)=F(5)F(1)
f ( 6 ) = F ( 6 ) − F ( 3 ) − F ( 2 ) + F ( 1 ) f(6)=F(6)-F(3)-F(2)+F(1) f(6)=F(6)F(3)F(2)+F(1)
f ( 7 ) = F ( 7 ) − F ( 1 ) f(7)=F(7)-F(1) f(7)=F(7)F(1)
f ( 8 ) = F ( 8 ) − F ( 4 ) f(8)=F(8)-F(4) f(8)=F(8)F(4)

那我们会发现,它只会由它因数的 F F F 值加减或者不要组成。

那我们可以把它弄成这样的形式:
f ( n ) = ∑ d ∣ n μ ( d ) F ( n d ) f(n)=\sum_{d|n}\mu(d)F(\frac{n}{d}) f(n)=dnμ(d)F(dn)

那我们的莫比乌斯函数 μ ( d ) \mu(d) μ(d) 就出现了!

定义

  1. 如果 d = 1 d=1 d=1,那 μ ( d ) = 1 \mu(d)=1 μ(d)=1
  2. 如果 d = p 1 p 2 . . . p k d=p_1p_2...p_k d=p1p2...pk p i p_i pi 是互不相同的素数,那就会有 μ ( d ) = ( − 1 ) k \mu(d)=(-1)^k μ(d)=(1)k
  3. 如果不满足上面两个条件,那 μ ( d ) = 0 \mu(d)=0 μ(d)=0

如何求

首先,我们可以很明显的看出用定义法求会比较慢,尤其是要求一个区间的。

我们可以考虑用类似 DP 的方法求。

看到跟素数有关,自然想到先用欧拉筛求。
然后我们可以考虑,在欧拉筛枚举最小质因子的时候,我们可以想到你处理 i × p r i m e j i\times prime_j i×primej 这个数。
那我们想 p r i m e j prime_j primej μ \mu μ 值已经求出,那我们可以看这个最小的质因子是否已经是 p r i m e j prime_j primej 的因子(就是能否整除)。
如果能整除,那就说明这个质数有两个,那就直接 μ i × p r i m e j = 0 \mu_{i\times prime_j}=0 μi×primej=0,那否则就是多一个素数,就是 μ i × p r i m e j = μ i × − 1 \mu_{i\times prime_j}=\mu_{i}\times-1 μi×primej=μi×1

你会想,啊,如果原来 p r i m e j prime_j primej 已经有一个素数能分解出两个或以上呢?
那因为这样,它的 μ \mu μ 值就一定是 0 0 0,就算乘了 − 1 -1 1,也还是 0 0 0,就没有问题了。

一些性质

  1. ∑ d ∣ n μ ( d ) = { 1    n = 1 0    n > 1 \sum_{d|n}\mu(d)=\left\{\begin{matrix} 1\ \ n=1\\ 0\ \ n>1 \end{matrix}\right. dnμ(d)={ 1  n=10  n>1
  2. φ ( n ) = ∑ d ∣ n μ ( d ) × n d \varphi(n)=\sum_{d|n}\dfrac{\mu(d)\times n}{d} φ(n)=dndμ(d)×n

关于这道题

我们考虑利用第一条性质:
a n s = ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = 1 ] ans=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[gcd(i,j)=1] ans=i=1nj=1m[gcd(i,j)=1]

a n s = ∑ i = 1 n ∑ j = 1 m [ gcd ⁡ ( i , j ) = 1 ] = ∑ i = 1 n ∑ j = 1 m ∑ d ∣ gcd ⁡ ( i , j ) μ ( d ) = ∑ i = 1 n ∑ j = 1 m ∑ d ∣ i , d ∣ j μ ( d ) = ∑ d ∑ i = 1 , d ∣ i n ∑ j = 1 , d ∣ j m μ ( d ) = ∑ d μ ( d ) ⋅ ⌊ n d ⌋ ⋅ ⌊ m d ⌋ \begin{aligned} ans &= \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=1]\\ &= \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sum\limits_{d|\gcd(i,j)}\mu(d)\\ &= \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sum\limits_{d|i,d|j}\mu(d)\\ &= \sum\limits_{d}\sum\limits_{i=1,d|i}^{n}\sum\limits_{j=1,d|j}^{m}\mu(d)\\ &= \sum\limits_{d}\mu(d)\cdot\left \lfloor \frac{n}{d} \right \rfloor\cdot\left \lfloor \frac{m}{d} \right \rfloor \end{aligned} ans=i=1nj=1m[gcd(i,j)=1]=i=1nj=1mdgcd(i,j)μ(d)=i=1nj=1mdi,djμ(d)=di=1,dinj=1,djmμ(d)=dμ(d)dndm

然后这就是 O ( n ) O(n) O(n) 的,但是因为它是多次询问,每个询问都是 O ( n ) O(n) O(n),就还是过不了。

然后看到向下取整,自然想到整除分块。
那就前缀和 μ \mu μ 函数,然后就整除分块处理就好了。

代码

#include<cstdio>
#include<iostream>
#define ll long long

using namespace std;

ll T;
ll n, m;
ll miu[100001], prime[100001];
ll qz[100001];
bool np[100001];

void get_miu() {
    
    //同欧拉筛预处理 μ
	miu[1] = 1;
	for (int i = 2; i <= 100000; i++) {
    
    
		if (!np[i]) {
    
    
			prime[++prime[0]] = i;
			miu[i] = -1;
		}
		for (int j = 1; j <= prime[0] && 1ll * i * prime[j] <= 100000ll; j++) {
    
    
			np[i * prime[j]] = 1;
			if (i % prime[j] == 0) {
    
    
				miu[i * prime[j]] = 0;
				break;
			}
			else miu[i * prime[j]] = miu[i] * -1;
		}
	}
}

void get_qz() {
    
    //前缀和
	for (int i = 1; i <= 100000; i++)
		qz[i] = qz[i - 1] + miu[i];
}

void work(int n, int m) {
    
    
	ll ans = 0;
	for (int l = 1; l <= n && l <= m; ) {
    
    
		int r = min(n / (n / l), m / (m / l));//数论分块加速
		ans += (qz[r] - qz[l - 1]) * (n / l) * (m / l);
		l = r + 1;
	}
	printf("%lld\n", ans);
}

int main() {
    
    
	get_miu();
	get_qz();
	
	scanf("%lld", &T);
	while (T--) {
    
    
		scanf("%lld %lld", &n, &m);
		work(n, m);
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/115253550