算法简介
莫比乌斯反演是组合数学中很重要的内容,可以用于解决很多组合数学的问题。
算法分析
先给个题目
[HAOI2011][BZOJ2301]Problem b
求满足a≤x≤b,c≤y≤d的所有数对(x,y)中,gcd(x,y)=k的数对的个数。
一个测试点总共有T组数据。
题目分析
简化问题
我们设f(k)(n,m)为满足1≤x≤n,1≤y≤m中所有数对(x,y)中,gcd(x,y)=k的数对个数。n,m在函数计算前已经给定,且n≤m。
那么答案显然就是f(k)(c,d)−f(k)(a−1,d)−f(k)(b,c−1)+f(k)(a−1,c−1)。
但是这样的f(k)很难直接在较优时间复杂度内求出,怎么办呢?
化难为易
我们发现f(k)很难直接求,那能不能用一个容易计算的东西来推出f(k)呢?
定义g(k)=∑⌊nk⌋i=1f(ik),即k|gcd(x,y)的数对(x,y)个数。
既然k|gcd(x,y),那么一定满足x=ak,y=bk(a,b∈N+)。a的取值有
也就是g(k)其实可以在O(1)的时间复杂度内计算。
那我们考虑如何通过g函数倒推
那么
难道我们用这条式子从大到小推出f(k)吗,其实这样对复杂度并没有什么太大的优化。
那f函数和
寻找联系
考虑这样一类函数(不要问我和上面有什么关系)
我们列举g(x)与f(x)之间的关系如下:
g(1)=f(1)
g(2)=f(1)+f(2)
g(3)=f(1)+f(3)
g(4)=f(1)+f(2)+f(4)
g(5)=f(1)+f(5)
g(6)=f(1)+f(2)+f(3)+f(6)
g(7)=f(1)+f(7)
g(8)=f(1)+f(2)+f(4)+f(8)
g(9)=f(1)+f(3)+f(9)
g(10)=f(1)+f(2)+f(5)+f(10)
尝试使用g(x)写出f(x):
f(1)=g(1)
f(2)=g(2)−g(1)
f(3)=g(3)−g(1)
f(4)=g(4)−g(2)
f(5)=g(5)−g(1)
f(6)=g(6)−g(3)−g(2)+g(1)
f(7)=g(7)−g(1)
f(8)=g(8)−g(4)
f(9)=g(9)−g(3)
f(10)=g(10)−g(5)−g(2)+g(1)
可以发现,f(x)的等式中所有g(y)都满足y|x。
那么是否有f(n)=∑d|ng(d)呢?显然不是,有的g(d)(d|n)没有出现在等式中,有的甚至是以相反数的形式出现。
那么我们可以猜测每个g(d)都乘上了一个系数。那么这个系数到底和什么有关呢?d?
莫比乌斯反演
基本知识
经过不断地猜想和验证,我们可以发现f貌似满足这样的关系式:
其中μ是一个函数,满足:
即当x能分解为若干互不相等的质数的乘积时(注意,不是质数的乘幂的乘积),设这样的质数有
μ函数的性质
性质1.1:μ是积性函数
证明:
对于数x,y满足gcd(x,y)=1,那么设x=∏ni=1piai,y=∏mi=1qibi,显然有∀1≤i≤n,1≤j≤m,满足gcd(pi,qj)=1。
若μ(x)=0,那么存在至少一个1≤i≤n使得ai>1,那xy分解后肯定存在一个因数为某质数的乘幂,因此μ(xy)一定为0,等于
μ(y)=0的情况类似。
μ(x)μ(y)≠0时,即μ(x)=(−1)n,μ(y)=(−1)m。xy肯定是同样的这些质数的乘积,因此μ(xy)=(−1)n+m,等于μ(x)μ(y)。
综上所述,μ(x)μ(y)=μ(xy)。
性质1.2:对n的因子的μ 值求和,当n=1时,和为1,否则为0
证明:
要证明的式子为
设n能分解为
考虑这些
研究过杨辉三角及二项式系数的人应该知道
在这里,我们令a=−1,b=1,则有
得证。
反演证明以及部分性质
反演证明
那么我们回到式子f(n)=∑d|ng(d)μ(nd)。之前我们说这是个猜想,那这个东西怎么证明呢?
套用g函数定义式得,证明上式即要证
更换主体,令T=dd1则有要证
莫比乌斯反演的常用形式
在实际运用中,很少直接用到莫比乌斯反演的定义形式。
我们通常遇到的问题是以这种形式出现的:
由此可以推到
证明和上面的证明类似:
积性函数
可以证明,当g为积性函数时,
这里只证明莫比乌斯反演基本形式的f,其变式类似。
证明:
令
线性求出μ函数
既然μ是积性函数,那么是否可以通过线性筛法线性地筛出μ的值呢?
显然:
∙对于p∈P,有μ(p)=−1
∙对于p∈P,a∈N∗,a>1,有μ(pa)=0
∙对于p,q∈N∗,gcd(p,q)=1,有μ(pq)=μ(p)μ(q)
∙特殊地,μ(1)=1
那么用线性筛法求μ的过程就显然了。
mu[1]=1;
pri[0]=0;
mark[1]=true;
for (int i=2;i<=N;i++)
{
if (!mark[i])
{
pri[++pri[0]]=i;
mu[i]=-1;
}
for (int j=1;j<=pri[0];j++)
{
if ((long long)i*pri[j]>N)
break;
mark[i*pri[j]]=true;
if (!(i%pri[j]))
{
mu[i*pri[j]]=0;
break;
}
mu[i*pri[j]]=-mu[i];
}
}
回到题目
莫比乌斯反演
题目中我们要求的目标函数为f(k),且有函数g满足
也就是
筛μ函数可以在O(n)的时间复杂度内解决,但是求f(k)时总不能枚举⌊nk⌋次吧,这样对时间复杂度并没有什么帮助,有什么好的办法求解呢?
分块大法好
我们考虑一个问题:∀d∈N∗,d∈[1,n]所有⌊nd⌋的取值有多少种?
∙∀d∈N∗,d∈[1,⌊n√⌋],显然d总共有
∙∀d∈N∗,d∈(⌊n√⌋,n],⌊nd⌋是小于等于n√的,那么⌊nd⌋肯定最多有n√种取值方法。
综上所述,∀d∈N∗,d∈[1,n]所有⌊nd⌋的取值最多就有2n√种。
现在思路就很显然了,我们对于i分块,使得每个块内
每个块内的μ值可以用前缀和处理,然后乘上相同的系数。
总的时间复杂度为O(n+n√),具体实现细节请看代码实现。
代码实现
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
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;
}
const int N=50000;
int mu[N+1],pri[N+1],sum[N+1];
int k,a,b,c,d,T;
bool mark[N+1];
void preparation()
{
pri[0]=0;
mark[1]=true;
mu[1]=1;
for (int i=2;i<=N;i++)
{
if (!mark[i])
{
pri[++pri[0]]=i;
mu[i]=-1;
}
for (int j=1;j<=pri[0];j++)
{
if ((long long)i*pri[j]>N)
break;
mark[i*pri[j]]=true;
mu[i*pri[j]]=-mu[i];
if (!(i%pri[j]))
{
mu[i*pri[j]]=0;
break;
}
}
}
sum[1]=mu[1];
for (int i=2;i<=N;i++)
sum[i]=sum[i-1]+mu[i];
}
int calc(int n,int m)
{
if (n>m)
n^=m^=n^=m;
int i=1,j,ret=0;
while (i<=n/k)
{
int j1=ceil(n/(k*(n/(k*i)))),j2=ceil(m/(k*(m/(k*i))));
j=min(j1,j2);
ret+=(sum[j]-sum[i-1])*(n/(k*i))*(m/(k*i));
i=j+1;
}
return ret;
}
int main()
{
preparation();
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
T=read();
while (T--)
{
a=read(),b=read(),c=read(),d=read(),k=read();
int ans1,ans2,ans3,ans4,ans;
ans1=calc(b,d);
ans2=calc(b,c-1);
ans3=calc(a-1,d);
ans4=calc(a-1,c-1);
ans=ans1-ans2-ans3+ans4;
printf("%d\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}
算法总结
回顾上面的题目,可以归纳出一般的莫比乌斯反演问题的解题步骤(是一般的,难题会比较坑)。
∙首先推导公式:根据题目大意,用准确的数学语言表述出求解的步骤(不要怕大暴力),这一步是解题的基础。
∙其次等价变换:有了求解式子之后,要想办法通过莫比乌斯反演的形式进行变形和优化。莫比乌斯反演的题目往往不会给你一个十分简单的求解式,最终式子的推出需要我们灵活运用换元、内外层求和对调等技巧,这其中认清我们要将哪一条式子作为主体,哪些式子作为系数,从而向所想的方向变化。
∙再次线性筛法:通常式子中的系数可以通过强大的线性筛法求出,这其中往往会运用到目标函数的积性。我们要注意分析和发现函数的性质。
∙最后分块大法:一般的莫比乌斯反演最后的式子都会出现形如⌊nd⌋的形式,这样就可以通过分块大法在n√的时间复杂度内解决。最后跟我高呼:“分块大法好,分块大法好,分块大法好!”