对于给出的 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核心思路:
莫比乌斯函数
首先我们需要了解莫比乌斯函数的基本定义:
还有就是怎么去求莫比乌斯函数,我们需要记住的是时刻从定义出发。然后接下来就是莫比乌斯的一个重要推导性质:
n=1 \(\sum_{d|n}u(d)=1\)
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中质因子的莫比乌斯函数的和.
当n=1时,\(S(n)=1\)
当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在一定的范围内取点。所以可以具化为这个图像:
标签:进阶,int,乌斯,sum,算法,莫比,我们,gcd From: https://www.cnblogs.com/xyh-hnust666/p/16960374.html我们要注意为什么我们又会想到面积呢,其实是因为\(\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; } }