莫比乌斯反演
前置知识
莫比乌斯反演的前置知识比较多,但比较碎,这里笔者当做回顾简单而已,如有莫比乌斯反演的需求,请跳过前置知识。 ,笔者也是一个初学者,如有错误,及时指出。感谢。
线性筛也是要会的。用来筛 \(\varphi , \mu\)
数论(整除)分块
用于加速统计答案的。 可以参考 \(LB\) 学长的博客 整除分块
我们发现在一个范围内 \(i \in [l , r]\) 使得 \(\lfloor \frac{k}{i} \rfloor\) 在 \(k\) 为定值得时候,其也为定值。那么如果我们将其 \(O(n)\) 的去加,我们显然复杂度可能是承受不了,那么我们可以选择分块来解决,也就是将使得 \(\lfloor\frac{k}{i}\rfloor\) 为定值的一段区间 \(i\) 分成一块来简化复杂度 。
\(Code\)
//注释是二维
for(qwq int l = 1 , r ; l <= n ; l = r + 1) // for(qwq int l = 1 , r ; l <= std::min(n , m) ; l = r + 1)
{
r = n / (n / l) ; // r = std::min(n / ( n / l ) , m / (m / l)) ;
····
}
欧拉函数 & 莫比乌斯函数
- \(\varphi(n)\) 表示的是在 \(1\to n\) 与 \(n\) 互质的个数 。 对于具体的欧拉函数,欧拉定理,不做阐述。
- \(\mu(n)\) 由莫比乌斯这个人提出的。 有 \(3\) 条如下的性质。
\(1.\) \(\mu(1) = 1\)
\(2.\) 若 \(n = \prod_{i = 1}^{k} p_i\) 其中 \(p_i\) 为质数并且每一项的指数为 \(1\) ,则 \(\mu(n) = (-1)^k\)
\(3.\) 其他情况为 \(0\)
求解 \(\mu\) 函数
void get_mu(int n) { // 1 -> n 的 mu 值
mu[1] = 1 ;
for(qwq int i = 2 ; i <= n ; i++)
{
if(!vis) prime[++cnt] = i , mu[i] = - 1 ;
for(qwq int j = 1 ; j <= cnt && i * prime[j] <= n ; j++)
{
vis[i * prime[j]] = true ;
if(i % prime[j]) == 0) {mu[i * prime[j]] = 0 ; break ;}
else mu[i * prime[j]] = - mu[i] ;
}
}
}
积性函数
-
对于两个数 \(n,m\) 互质且 \(f(n\times m) = f(n)\times f(m)\) 则称 \(f(x)\) 为积性函数。
-
还有就是任意两个数 \(n,m\) 且 \(f(n\times m) = f(n)\times f(m)\) 则称 \(f(x)\) 为完全积性函数。
举个例子 : \(\varphi(n)\) 就是一个积性函数, \(\mu(n)\) 也是一个积性函数。这里给出 \(\mu(n)\) 为积性函数的证明:
\(1.\) 根据上文的 \(\mu\) 的性质 \(1,3\) 的情况,这显然的是情况的。
\(2.\) 对于上文的 \(\mu\) 的性质 \(2\) 的情况 (笔者的证明显然不是特别的严谨)
对于一个数 \(n,m\) 我们将其分解为 \(n = \prod_{i = 1}^{k_1} p_i , m = \prod_{j = 1}^{k_2}q_i\) (其中 \(p_i , q_i\)) 均为质数 。 则 \(f(n) = (-1)^{k_1} , f(m) = (-1)^{k_2} , f(n\times m) = (-1)^{k_1 + k_2} = (-1)^{k_1} \times (-1)^{k_2} = f(n) \times f(m)\) ,则得证 。
狄里克雷卷积
定义两个数论函数 \(f,g\) 的卷积为 :
三个性质和恒等式
-
\[f \ast g = g \ast f \]
-
\[(f \ast g) \ast h = f \ast (g \ast h) \]
-
\[(f + g) \ast h = f \ast h + g \ast h \]
\[我是来分开他们的 \]
正文
其中可能结合博客 【化简式子】 食用效果更佳。
扔一个定理
对于 \(F(n)\) 和 \(f(n)\) 是定义在非负整数集合上的两个函数,并且满足 \(F(n) = \sum_{d|n}f(d)\)
那么我们就得到结论:
证明:
\[\sum_{d|n}\mu(d)F(\frac{n}{d}) = \sum_{d|n}\mu(d)\sum_{d' |\frac{n}{d}}f(d') = \sum_{d'|n}f(d')\sum_{d | \frac{n}{d'}}\mu(d) = f(n) \]
这就是莫比乌斯反演定理。
P2568 GCD
【description】:
求解 \(\sum_{i = 1}^{n}\sum_{j = 1}^{m}[gcd(i , j)为质数]\) , \(n \leq 10^7\)
【solution】:
我们开始化简式子:
我们是很容易得到 :
你以为这样就 \(OK\) 了,显然是不行的,我们枚举 \(p\) 大约是 \(10^5\)级别 再加上枚举 \(d\) 哪怕我们选择用数列分块,也逃不了 \(10^3\) ,我们还有 \(10^4\) 的级别的询问,总共应该是 \(10^{12}\) ,我们需要进一步的进行化简式子。
采取换元。
我令 \(T = dp\) ,那么我们就将原式化成了
我们发现后两项跟 \(d, p\) 没有关系了,我们将其提出来就有:
后面的式子可以预处理出来,并且前面的式子可以用数列分块搞一下。
【Code】
/*
By : Zmonarch
知识点:
手握日月摘星辰,世间无我这般人!
*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <stack>
#include <iostream>
#define inf 2147483647
#define qwq register
//#define int long long
const int kmaxn = 1e7 + 10 ;
const int kmod = 998244353 ;
inline int read() {
int x = 0 , f = 1 ; char ch = getchar() ;
while(!isdigit(ch)) {if(ch == '-') f = - 1 ; ch = getchar() ;}
while( isdigit(ch)) {x = x * 10 + ch - '0' ; ch = getchar() ;}
return x * f ;
}
int cnt ;
int mu[kmaxn] , prime[kmaxn] ;
bool vis[kmaxn] ;
long long sum[kmaxn] , f[kmaxn] ;
void init(int n) {
mu[1] = 1 ;
for(qwq int i = 2 ; i <= n ; i++)
{
if(!vis[i]) prime[++cnt] = i , mu[i] = - 1 ;
for(qwq int j = 1 ; j <= cnt && i * prime[j] <= n ; j++)
{
vis[i * prime[j]] = true ;
if(i % prime[j] == 0) {mu[i * prime[j]] = 0 ; break ; }
else mu[i * prime[j]] = - mu[i] ;
}
}
for(qwq int i = 1 ; i <= cnt ; i++)
for(qwq int j = 1 ; j * prime[i] <= n ; j++)
f[j * prime[i]] += mu[j] ;
for(qwq int i = 1 ; i <= n ; i++)
sum[i] = sum[i - 1] + f[i] ;
}
long long query(int n , int m) {
long long ret = 0 ;
for(qwq int l = 1 , r ; l <= std::min(n , m) ; l = r + 1)
{
r = std::min((n / (n / l)) , (m / (m / l))) ;
ret += (sum[r] - sum[l - 1]) * 1ll * (n / l) * 1ll * (m / l) ;
}
return ret ;
}
signed main() {
init(kmaxn - 10) ;
int T = read() ;
while(T--)
{
int n = read() , m = read() ;
printf("%lld\n" , query(n , m)) ;
}
return 0 ;
}
UVA11424 GCD - Extreme (I)
【description】 :
求解 \(\sum_{i = 1}^{n}\sum_{j = i + 1} ^{n} gcd(i , j)\)
多倍经验 : UVA11417 GCD,UVA11424 GCD - Extreme (I),P1390 公约数的和,P2398 GCD SUM,P2568 GCD,SP3871 GCDEX - GCD Extreme ,UVA11426 拿行李(极限版)
有的直接可过,有的略微改改就可过
【solution】:
化简式子 :
然后我们就能得到
最后答案就为
直接搞就行了。
【Code】
/*
By : Zmonarch
知识点:
手握日月摘星辰,世间无我这般人!
*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <stack>
#include <iostream>
#define inf 2147483647
#define qwq register
#define int long long
const int kmaxn = 4e6 + 10 ;
const int kmod = 998244353 ;
inline int read() {
int x = 0 , f = 1 ; char ch = getchar() ;
while(!isdigit(ch)) {if(ch == '-') f = - 1 ; ch = getchar() ;}
while( isdigit(ch)) {x = x * 10 + ch - '0' ; ch = getchar() ;}
return x * f ;
}
int cnt , k ;
int prime[kmaxn] , phi[kmaxn] , sum[kmaxn];
bool vis[kmaxn] ;
void eular(int n){
phi[1] = 1 ; sum[1] = 1 ;
for(qwq int i = 2 ; i <= n ; i++)
{
if(!vis[i]) phi[i] = i - 1 , prime[++cnt] = i ;
for(qwq int j = 1 ; j <= cnt && i * prime[j] <= n ; j++)
{
vis[i * prime[j]] = true ;
if(i % prime[j] == 0) {phi[i * prime[j]] = phi[i] * prime[j] ; break ;}
else phi[i * prime[j]] = phi[i] * phi[prime[j]] ;
}
sum[i] = sum[i - 1] + phi[i] ;
}
}
void solve(int n) {
int ret = 0 ;
for(qwq int l = 1 , r ; l <= n ; l = r + 1)
{
r = n / (n / l) ;
ret += ( 1ll * (n / l) * (1ll * n / l - 1) / 2) * (sum[r] - sum[l - 1]) ;
}
printf("%lld\n" , ret) ;
}
signed main() {
eular(kmaxn - 5) ;
while(1)
{
int n = read() ;
if(!n) return 0 ;
solve(n) ;
}
return 0 ;
}