算法进阶课

对于给出的 n 个询问,每次求有多少个数对 (x,y),满足 a≤x≤b,c≤y≤d,且 gcd(x,y)=k,gcd(x,y) 函数为 x 和 y 的最大公约数。

输入格式

第一行一个整数 n。

接下来 n 行每行五个整数,分别表示 a、b、c、d、k。

输出格式

共 n 行,每行一个整数表示满足要求的数对 (x,y) 的个数。

数据范围

1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000

输入样例:

2
2 5 1 5 1
1 5 1 5 2

输出样例:

14
3

核心思路

莫比乌斯函数

首先我们需要了解莫比乌斯函数的基本定义:

还有就是怎么去求莫比乌斯函数,我们需要记住的是时刻从定义出发。然后接下来就是莫比乌斯的一个重要推导性质:

  1. n=1 \(\sum_{d|n}u(d)=1\)

  2. n!=1 \(\sum_{d|n}u(d)=0\)

下面是相关的证明:

假设\(n=\prod_{i=1}^{k} p_i^{c_i}\),\(n^{''}=\prod_{i=1}^{k} p_i\).

所以\(\sum_{d|n}u(d)=\sum_{d|n^{''}} u(d)=\sum_{i=0}^{k}C_{k}^{i}*(-1)^k\)

这个后面的主要是我们对于一个质因子的选择,比如选一个,选两个。然后对应的u(d)也要发生变化。然后第一个等于号怎么成立呢。因为d肯定是n的质因数。所以必然是可以成立的。

然后其实我们可以使用\(S(n)=\sum_{d|n} u(d)\),用通俗易懂的话解释就是\(S(n)\)是n中质因子的莫比乌斯函数的和.

  1. 当n=1时,\(S(n)=1\)

  2. 当n!=1时,\(S(n)=0\)


莫比乌斯反演


问题解决

然后我们这里有个重要的思想,那就是把我们的实际问题转换为对应的模型,也就是把那个实际问题给用F(n),f(n)表示出来。其中我们的F(n)可以表示为\(\sum_{i=1}^{a}\sum_{j=1}^{b}[n|(i,j)]\),\((i,j)\)表示的是gcd(i,j).F(n)的是实际意义就是i,j最大公约数是n的倍数的个数。所以我们f(n)就需要是\(\sum_{i=1}^{a}\sum_{j=1}^{b}gcd(i,j)\),这个我们可以根据莫比乌斯反演来验证其正确性。其实我们可以根据f(n)的实际意义来反推F(n)的表达式。

然后我们吧F(n)化简方便后面套用公式。

我们可以知道F(n)枚举的是gcd是n的倍数。而[1,a],n的倍数是n/a,[1,b]n的倍数是n/b;然后使用组合乘法:F(n)=\(\lfloor{n/a}\rfloor *\lfloor{n/b}\rfloor\).然后我们套用公式化简:

然后我们接下来的下取整就是数论分块的知识。不记得就去看数论的博客。所以我们知道a'/d’在[l,r]中的值不变,所以我们才需要求u(x)的前缀和,方便计算。

接下来就还有一个二维前缀和的知识点,因为我们需要在x和y在一定的范围内取点。所以可以具化为这个图像:

我们要注意为什么我们又会想到面积呢,其实是因为\(\sum_{i=1}^{a} \sum_{j=1}^{b}\)表示的本来就是二重积分,而我们二重积分的集合意义就是面积。然后把这个面积表示出来就好了。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int primes[N], st[N];
int mobius[N],sum[N];
int cnt;
void Init()
{
    mobius[1]=1;//这个初始化一定需要主意啊
	for (int i = 2;i < N;i++)
	{
		if (st[i] == 0)
		{
			primes[cnt++] = i;
			mobius[i] = -1;//因为这是一个单纯的质数。没有任何质因子
		}
		for(int j=0;primes[j]*i<N;j++)
		{
			st[i * primes[j]] = 1;
			int t = i * primes[j];
			if (i % primes[j] == 0)
			{
				mobius[t] = 0;//因为这个说明至少包含两个primes[j]
				break;
			}
			mobius[t] = -mobius[i];//这个因为我们加入了一个系数为1的质因子
		}
	}
	for (int i = 1;i < N;i++)
		sum[i] = sum[i - 1] + mobius[i];
}
int get(int a, int k)
{
	return a / (a / k);
}
LL f(int a, int b, int k)
{
	a = a / k, b = b / k;
	LL res = 0;
	int n = min(a, b);
	for (int l = 1, r;l <= n;l = r + 1)
	{
		r = min(n, min(get(a, l), get(b, l)));
		res += (LL)(sum[r] - sum[l - 1]) * (a / l) * (b / l);
	}
	return res;
}
int main()
{
	int T;
	cin >> T;
	Init();
	while (T--)
	{
		int a, b, c, d, k;
		cin >> a >> b >> c >> d >> k;
		cout << f(b, d, k) - f(a-1, d, k) - f(b, c-1, k) + f(a - 1, c - 1, k) << endl;
	}
}
posted @ 2022-12-06 20:02  努力的德华  阅读(41)  评论(0)    收藏  举报