介绍
数论函数就是丢进去一个整数,丢出来一个整数的函数。很简单吧?(原因是我讲的有点俗。。。)
几个定义:
积性函数:一个数论函数
满足:对于任意互质的p,q,
完全积性函数:同上,且p,q不互质时也满足
狄利克雷卷积
两个函数
和
,它们的卷积就是因数对的值相乘再相加。如
。写成式子的形式就是
。
显然,我们发现,这是对称的,所以狄利克雷卷积满足交换律和结合律。
几个常见的函数
1.epsilon( ):单位函数,类似乘法中的"1",对任意数论函数 满足
2. :恒等函数,传什么进去都是返回1
3. :传多少返回多少,即
4.(开始复杂了)phi( ),欧拉函数,返回1~n中有多少和n互质。
5.(主角) mu( ),莫比乌斯函数,返回
若n=1,返回1,否则
若n有一个因数是个不为1的完全平方数 ,则
否则,不难发现,n分解质因数后应该每一个指数都是1。此时 ,其中k是n的质因子个数
举(几)个栗子:
上面几个函数都是积性函数
讲反演之前:
线性筛mu
mu是积性函数,和质数筛类似,时间复杂度也是O(n)。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 1001000
using namespace std;
int primes[N];bool notp[N];
int mu[N];
void Init()
{
int cnt=0;//当前多少质数
int n=1000000;//规模(如果没有空间限制,珂以到1e7)
notp[1]=mu[1]=1;//边界
for(int i=2;i<=n;i++)
{
if (!notp[i])//如果i是质数
{
primes[++cnt]=i;//记录
mu[i]=-1;//显然(因为此时k=1)
}
for(int j=2;j<=cnt and i*primes[j]<=n;j++)
{
int u=primes[j];//临时取出(为了代码更短)
notp[i*u]=1;//i*u显然不会是质数
if (i%u==0)
{
mu[i*u]=0;
//此时因为i是u的倍数,所以i*u中至少包含两个u,即i*u是u*u的倍数,此时显然mu值为0
break;
}
else
{
mu[i*u]=-mu[i];//即mu[i*u]=mu[i]*mu[u],由于mu[u]=-1,所以也珂以写成这个样子
}
}
}
}
整除分块
如何快速求
?
打表找一下规律。把n设置成30,看看每个n/i的值。
结果:
30 15 10 7 6 5 4 3 3 3 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
后面好长一段1啊!2,3也有一段,到4就是一个一个了。但是会不会这只是特殊情况呢?
把1~100打出来看看吧。
结果:
100 50 33 25 20 16 14 12 11 10 9 8 7 7 6 6 5 5 5 5 4 4 4 4 4 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
一看:WA!好多一样啊!
找一下规律:如果当前块的起点是l
,那么这个块的终点就是n/(n/l)
。这个子问题的代码如下(也可以作为整除分块的模板):
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main()
{
ll n;
scanf("%lld",&n);
int ans=0;
for(ll l=1,r;l<=n;l=r+1)
{
r=n/(n/l);//获取右边
ans+=(n/l)*(r-l+1);//优化:不用一个一个加n/l,而是*(r-l+1),加快了很多
}
printf("%lld\n",ans);
}
复杂度是 的
莫比乌斯反演
其实不用背那么多式子的吧。。。我觉得只要记得两个就够了。
(第1个更加常用,第二个也会有,也要了解,不过下面没有第二个式子的用法介绍)
如果你不信这两个个式子万能,请看题:
洛谷P3455 [POI2007]ZAP-Queries
(这个题目十分经典,珂以说是莫比乌斯反演的模板题)
(我没有把它写到做题笔记里面,原因是这个是模板,要放在学习笔记里面讲)
(别的题目可能会放在做题笔记里面)
题意概括:求
(后为了方便设 )
此处顺便讲一下如何对带有sigma的是做变换。
首先,不妨令n<m(为什么不妨?原因是n,m对称)
我们枚举
,
,显然
和
的
肯定
,如果i和j互质的话,就
了。
所以珂以换成这样的枚举:
这就相当于i,j是在枚举d的倍数。我们发现,d的倍数在1~n中应该有 个,m中同理。原式化为:
然后,那个 珂以换成 (根据 的定义得),然后再换一下 ,原式化为
接下来是思维难点:我们如何把q放到外面枚举?
(很多博客都没讲,害的蒟蒻理解了1个月才搞懂)
(如果您像博主一样也是个蒟蒻,请先背下来式子,然后反复做这一类的题目,有时间手动模拟一下这个换枚举的过程,慢慢就懂了)
我们珂以在外面先枚举上q,然后过会在尻♂虑哪些i,j会算到q。显然,满足
的一对i,j会算到一次q。则原式化为:
我们按刚刚类似的方法,枚举g的倍数,珂以得到:
发现对于任意的g都满足 ,所以这个珂以省略不写
又由于 和i,j没有关系,所以珂以提到前面去。原式化为:
到这里发现珂以整除分块, 一遍过。
代码:
//有一些东西上面没有讲,代码里面会有简单的注释,如果理解不了。。。那就背下来,再多看几遍
#include<bits/stdc++.h>
#define int long long
#define N 1001000//没有这么大,但是习惯开这么大(2333)
using namespace std;
int primes[N];bool notp[N];
int mu[N];
void Init()
{
//筛mu
int cnt=0;
int n=1000000;
notp[1]=1;
mu[1]=1;
for(int i=2;i<=n;i++)
{
if (!notp[i])
{
primes[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt and i*primes[j]<=n;j++)
{
int u=primes[j];
notp[i*u]=1;
if (i%u==0)
{
mu[i*u]=0;
break;
}
else
{
mu[i*u]=-mu[i];
}
}
}
//这边记录前缀和,方便后面整除分块
for(int i=1;i<=n;i++)
{
mu[i]+=mu[i-1];
}
}
int n,m,d;
void Input()
{
scanf("%lld%lld%lld",&n,&m,&d);
}
void Solve()
{
if (n>m) swap(n,m);//不妨令n<=m
int ans=0;
for(int l=1,r;l<=n;l=r+1)
{
r=min(n/(n/l),m/(m/l));//两个同时分块一定要取最小(自己画图找规律)
ans+=(n/(d*l))*(m/(d*l))*(mu[r]-mu[l-1]);//只要把n/dl*m/dl优化成*(mu[r]-mu[l-1]),即l~r的mu值和,就不用一个一个加了。
}
printf("%lld\n",ans);
}
main()
{
Init();//预处理
int T;scanf("%lld",&T);
while(T-->0)//这个相当于T--,T-->0就是为了好看
{
Input();
Solve();
}
return 0;
}
结束语
如果您看懂了刚刚的胡扯,那您应该已经入门莫比乌斯反演了,也对数论函数和sigma的变换有了一定的了解。当然,后续还会在做题笔记中也会提到跟复杂的变换,做好准备了么?