![0cfd3bfcc82870d3c42ee6aac84d9830.png](https://i-blog.csdnimg.cn/blog_migrate/640b3d2210769b4d5681aec7c9861b7c.jpeg)
写在最前
生活所迫。数论小白开始入门数论了。
会陆陆续续发一些自己的笔记和总结。
原文链接
莫比乌斯反演学习笔记xiejiadong.com![da7f3d266bc4a199ae613d63136aeb9d.png](https://i-blog.csdnimg.cn/blog_migrate/7195eb7b61861e0f01d53a87c7026c68.jpeg)
数论函数
定义域为正整数的函数称为数论函数。
积性函数
如果
常见的数论函数:
- 欧拉函数(如果
,则有
)
- 莫比乌斯函数
- 除数函数 ,用
表示。其值等于所有
的因子的
次方之和。
完全积性函数
如果
常见的完全积性函数有:
Dirichlet 卷积
两个数论函数
其中 Dirichlet 卷积的单位元定义为
【结论】如果
莫比乌斯函数
如果
【性质】
【证明】我们令。
显然,枚举的因子和枚举
因子的差异就在于少枚举了含有平方因子的因子。
则有。
此时的,我们就可以考虑在
中任意选取组成一个因子了,于是就有
。
也就是只有当的时候
,其他时候
。
而显然当的时候,
,于是就证明了。
莫比乌斯反演
设
【证明】因为我们有,而
其实就是
。
于是即
也就是
。
不过一般情况下构造一个
一种比较常见的问题是这样的:求
我们考虑枚举
考虑将
我们令
则有
【例1】HAOI2011 Problemb
题意
求
分析
因为
我们令
于是就有
因为有多组询问,这样的时间复杂度仍然是不允许的。
我们可以考虑数论分块,所谓数论分块,无非是
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL p_max = 100010;
LL mu[p_max];
void get_mu() {
mu[1] = 1;
static bool vis[p_max];
static LL prime[p_max], p_sz, d;
for (int i=2;i<p_max;i++)
{
if (!vis[i]) {
prime[p_sz++] = i;
mu[i] = -1;
}
for (LL j = 0; j < p_sz && (d = i * prime[j]) < p_max; ++j) {
vis[d] = 1;
if (i % prime[j] == 0) {
mu[d] = 0;
break;
}
else mu[d] = -mu[i];
}
}
}
LL T,a,b,n,m,k,f[p_max];
LL calc(LL n,LL m)
{
if(n>m) swap(n,m);
LL ans=0;
for (LL i=1;i<=n/k;)
{
LL p=n/i/k,q=m/i/k;
p=min(n/k,n/p/k),q=min(n/k,m/q/k);
p=min(p,q);
ans+=(f[p]-f[i-1])*(n/i/k)*(m/i/k);
//cout<<"p,q="<<p<<" "<<q<<endl;
//cout<<i<<" "<<" "<<(f[p]-f[i-1])<<" "<<(f[p]-f[i-1])*(n/i/k)*(m/i/k)*(p-i+1)<<endl;
i=p+1;
}
return ans;
}
void init()
{
f[0]=0;
for (int i=1;i<p_max;i++)
f[i]=f[i-1]+mu[i];
}
int main()
{
get_mu();init();
scanf("%lld",&T);
while(T--)
{
scanf("%lld%lld%lld%lld%lld",&a,&n,&b,&m,&k);
printf("%lldn",calc(n,m)-calc(a-1,m)-calc(n,b-1)+calc(a-1,b-1));
}
return 0;
}
【例2】SPOJ5971 LCMSUM
题意
求
分析
我们并不太会直接求 lcm ,于是考虑转换成 gcd 来做。
对于 gcd 而言,显然有
考虑枚举
于是有
令
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL p_max = 1000100;
LL phi[p_max];
void get_phi() {
phi[1] = 1;
static bool vis[p_max];
static LL prime[p_max], p_sz, d;
for (int i=2;i<p_max;i++){
if (!vis[i]) {
prime[p_sz++] = i;
phi[i] = i - 1;
}
for (LL j = 0; j < p_sz && (d = i * prime[j]) < p_max; ++j) {
vis[d] = 1;
if (i % prime[j] == 0) {
phi[d] = phi[i] * prime[j];
break;
}
else phi[d] = phi[i] * (prime[j] - 1);
}
}
}
LL T,n,f[p_max],g[p_max];
int main()
{
get_phi();
for (int i=1;i<p_max;i++)
f[i]=phi[i]*i;
memset(g,0,sizeof(g));
for (int i=2;i<p_max;i++)
for (int j=i;j<p_max;j+=i)
g[j]+=f[i];
scanf("%lld",&T);
while(T--)
{
scanf("%lld",&n);
printf("%lldn",g[n]*n/2LL+n);
}
return 0;
}
【例3】hdu4944 FSF’s game
题意
求
分析
因为
显然
上一个例题解决了
于是现在就是求
Tle 了两发。过不去。只能考虑优化。
我们考虑枚举
假设当前
可以考虑在前后打上标记,然后前缀和预处理计算。
此时复杂度变成了
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL p_max = 500100;
LL phi[p_max];
void get_phi() {
phi[1] = 1;
static bool vis[p_max];
static LL prime[p_max], p_sz, d;
for (int i=2;i<p_max;i++){
if (!vis[i]) {
prime[p_sz++] = i;
phi[i] = i - 1;
}
for (LL j = 0; j < p_sz && (d = i * prime[j]) < p_max; ++j) {
vis[d] = 1;
if (i % prime[j] == 0) {
phi[d] = phi[i] * prime[j];
break;
}
else phi[d] = phi[i] * (prime[j] - 1);
}
}
}
LL T,n,f[p_max],g[p_max],sum[p_max];
int main()
{
get_phi();
for (int i=1;i<p_max;i++)
f[i]=phi[i]*i;
memset(g,0,sizeof(g));
for (int i=2;i<p_max;i++)
for (int j=i;j<p_max;j+=i)
g[j]+=f[i];
for (int i=1;i<p_max;i++)
f[i]=g[i]*i/2LL+i;
f[0]=0;
for (int i=1;i<p_max;i++)
f[i]+=f[i-1];
for (LL d=1;d<p_max;d++)
{
for (LL i=1;i*d<p_max;i++)
{
sum[d*i]+=d*d*f[i];
if (d*(i+1)<p_max) sum[d*(i+1)]-=d*d*f[i];
}
}
sum[0]=0;
for (int i=1;i<p_max;i++)
sum[i]+=sum[i-1];
scanf("%lld",&T);int cass=0;
while(T--)
{
scanf("%lld",&n);
printf("Case #%d: %un",++cass,(unsigned)sum[n]);
}
return 0;
}
【例4】湖北省队互测 一个人的数论
题意
求所有
分析
题目要求的就是
用莫比乌斯反演
变换求和的顺序
显然
我们令
于是原式就变成了
变换求和顺序变成
显然
因为
我们令
考虑对于任意的
#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL bin(LL x,LL n,LL MOD)
{
LL ret=MOD!=1;
for (x%=MOD;n;n>>=1,x=x*x%MOD)
if (n&1) ret=ret*x%MOD;
return ret;
}
const LL MOD=1000000007;
const int MAXN = 1005;
typedef vector<LL> Poly;
Poly operator * (const Poly &a, const Poly &b) {
Poly c(a.size() + b.size() - 1, 0);
int an = a.size(), bn = b.size();
for (int i=0; i<an; ++i)
for (int j=0; j<bn; ++j)
c[i+j] = (c[i+j] + a[i]*b[j])%MOD;
while (!c.empty() && c.back() == 0) c.pop_back();
return c;
}
Poly operator + (const Poly &a, const Poly &b)
{
int an = a.size(), bn = b.size(), n = max(an, bn); Poly c; c.resize(n);
for (int i=0; i<n; ++i)
{
c[i] = 0;
if (i < an) c[i] += a[i];
if (i < bn) c[i] += b[i];
while (c[i] >= MOD) c[i] -= MOD;
}
while (!c.empty() && c.back() == 0) c.pop_back();
return c;
}
void operator *= (Poly &a, LL b){
int n = a.size();
for (int i=0; i<n; ++i)
a[i] = a[i] * b % MOD;
}
Poly g[MAXN];
LL c[MAXN];
Poly go(LL x[], LL y[], int n) {
for (int i=0; i<n; ++i){
LL B = 1; g[i].push_back(1);
for (int j=0; j<n; ++j) {
if (i == j) continue;
B = B * (x[i] - x[j] + MOD) % MOD;
Poly p;p.push_back(MOD-x[j]);p.push_back(1);
g[i] = g[i] * p;
}
g[i] *= (MOD + y[i]) * bin(B, MOD-2 ,MOD)%MOD;
}
Poly res;res.clear();
for (int i=0; i<n; ++i) { res = res + g[i]; }
return res;
}
LL n,d,w,x[MAXN],y[MAXN],p[MAXN],a[MAXN];
int main()
{
scanf("%lld%lld",&d,&w);
n=1;
for (int i=1;i<=w;i++)
scanf("%lld%lld",&p[i],&a[i]),n*=bin(p[i],a[i],MOD),n%=MOD;
x[0]=0;y[0]=bin(0,d,MOD);
for (int i=1;i<=d+1;i++)
x[i]=i,y[i]=(y[i-1]+bin(i,d,MOD))%MOD;
Poly res=go(x,y,d+2);
LL ans=0;
for (int i=0;i<=d+1;i++)
x[i]=res[i];
for (int i=0;i<=d+1;i++)
{
LL sum=1;
for (int j=1;j<=w;j++)
{
LL x=p[j],y=a[j];
sum=(sum*((bin(bin(x,y,MOD),i,MOD)%MOD-bin(x,d,MOD)*bin(bin(x,y-1,MOD),i,MOD)%MOD+MOD)%MOD))%MOD;
//cout<<((mu[1]*bin(x,i,MOD)%MOD+mu[x]*bin(x,d,MOD)%MOD)%MOD)<<endl;
}
ans+=x[i]*sum;
ans%=MOD;
//printf("x[i]=%lld sum=%lld ans=%lldn",x[i],sum,ans);
}
printf("%lldn",ans);
return 0;
}