积性函数

线性筛与积性函数

定义:积性函数指对于所有互质的整数a和b有性质\(f(ab)=f(a)f(b)\)的数论函数(\(a,b\)在正整数域内,以下一切讨论都在正整数域内)。


常见的积性函数:

- \(\varphi(n)\) 欧拉函数(在\(1 \leq x \leq n\)区间内,使得\(gcd(x,n)=1\)成立的\(x\)的个数)

- \(\mu(n)\) 莫比乌斯函数(定义:令\(n=a_{1}^{b_1}a_{2}^{b_2}...a_{t}^{b_t}\),若\(\sum_{i=1}^{n}[b_{i}>=2]>0\),则\(\mu(n)=0\),否则\(\mu(n)=(-1)^t\),特别的\(\mu(1)=1\))

- \(d(n)\) 约数个数函数 (\(\sum_{d|n}1\))

- \(\sigma(n)\) 约数和函数 (\(\sum_{d|n}d\))

辅助、基本积性函数:

- \(\varepsilon(n)=[n=1]\) (一元函数)

- \(id(n)=n\) (单位函数)

- \(I(n)=1\) (恒等函数)

具体用法使用杜教筛时会用到


狄利克雷卷积:

定义:设\(f(n),g(n)\)是两个积性函数(数论函数),则它们的狄利克雷卷积\(h(n)=\sum_{d|n}f(d)g(\frac{n}{d})\),且它是积性函数(数论函数)

注:狄利克雷卷积的另一种表达方式\(h(n)=\sum_{ab=n}f(a)g(b)\)


莫比乌斯函数:

性质证明:

1. \(\mu*I=\varepsilon\)

证明:\(\sum_{d \mid n}\mu(d)I(\frac{n}{d})=\sum_{d \mid n}\mu(d)\)
因为当\(n=1\)时,上式\(=1\),显然成立,所以我们只需要证明当\(n>1\)时,上式\(=0\)
我们不妨设\(n=\prod_{i=1}^{k}p_k^{a_k}\),则对于任意的\(d \mid n\)都有\(d=\prod_{i=1}^{t}p_t^{v_t} (t\leq k,v_t\leq a_k)\)
根据定义得当\(v_t>1\)时,\(\mu(d)=0\),所以当\(v_t=1\)时,\(\mu(d)=\pm1\)
其实就是相当于在\(k\)\(-1\)中,选择\(t\)个,所以\(\sum_{d \mid n}\mu(d)=\sum_{i=0}^{n}C(k,i)=0\)
所以\(sum_{d \mid n}\mu(d)=[n=1]\)

2. 若\(F(n)=\sum_{d \mid n}f(d)\),则\(f(n)=\sum_{d \mid n}\mu(d)F(\lfloor \frac{n}{d} \rfloor)\) [莫比乌斯反演]

证明:\(\therefore f(n)=\sum_{d|n}\mu(d)\sum_{e|\frac{n}{d}}f(e)\)
由分配律我们可以知道\(f(n)=\sum_{d|n}\sum_{e|\frac{n}{d}}\mu(d)f(e)\)
因为稍微理解一下,发现其实是所有满足\((d⋅e)|n\)的二元组\((d,e)\)之于是可以交换前两个\(\sum\)的位置即,所以\(f(n)=\sum_{e|n}\sum_{d|\frac{n}{e}}\mu(d)f(e)\)
再由分配律逆定理可知\(f(n)=\sum_{e|n}f(e)\sum_{d|\frac{n}{e}}\mu(d)\)
所以\(f(n)=\sum_{e|n}f(e)*[\frac{n}{e}=1]\),所以只有当\(n=e\)时,取值才有效,所以\(f(n)=f(n)=\sum_{d \mid n}\mu(d)F(\lfloor \frac{n}{d} \rfloor)\)

线性筛莫比乌斯函数:

做法:在线性筛的基础上加上,若\(i\)为质数,则\(\mu(i)=-1\),之后根据他是积性函数的定义,则对于的\(i \mod p_j \not =0(p为小于i的质数集和)\),都有\(\mu(i*p_j)=\mu(i)*\mu(p_j)\),若是\(i \mod p_j=0\),则有至少一个二次以上的指数,所以\(\mu(i*p_j)=0\).

代码实现:

#include<map>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 20000000
#define int long long
using namespace std;
void read(int &sum)
{
	sum=0;char last='w',ch=getchar();
	while (ch<'0' || ch>'9') last=ch,ch=getchar();
	while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
	if (last=='-') sum=-sum;
}
int mu[maxn+5];
int p[maxn+5],bz[maxn+5],len;
void find_prime()
{
	mu[1]=1;
	for (int i=2;i<=maxn;i++)
	{
		if (bz[i]==false) mu[i]=-1,len++,p[len]=i;
		for (int j=1;j<=len && i*p[j]<=maxn;j++)
		{
			bz[i*p[j]]=1;
			if (i%p[j]==0) { mu[i*p[j]]=0; break; }
			else mu[i*p[j]]=mu[i]*mu[p[j]];
		}
	}
}
signed main()
{
//	freopen("M.in","r",stdin);
//	freopen("M.out","w",stdout);
	find_prime();
//	fclose(stdin);fclose(stdout);
	return 0;
}

欧拉函数:

性质证明:

1. 若\(p\)为质数,则\(\varphi(p^k)=p^k-p^{k-1}\)

证明:\(1\)~\(p^k\)总共有\(p^k\)个数,因为\(p\)是质数,所以其中与\(p\)不互质的只有\(p\)的倍数,而\(p^k\)中有\(p^k/k\)个倍数,所以互质数就为\(p^k-p^{k-1}\)

2. \(\varphi*I=id\)

证明:给出\(\frac{1}{n},\frac{2}{n},\frac{3}{n}...,\frac{n}{n} n\)个分数,把他们化为最简形式\(\frac{a}{b}\),那什么\(a,b\)要有什么条件呢?经过观察发现,\(b \mid n , gcd(a,b)=1\),那么以\(b\)为分母的数就有\(\varphi(a)\)个,又因为\(b \mid n\),所以\(\sum_{d|n}\varphi(d)=n(总共有n个分数)\),所以\(\varphi*I=id\)

3. \(\varphi(p^k)=\varphi(p^{k-1})*p\)

证明:\(\because \varphi(p^k)=p^{k-1}*(p-1)\)

\(\because \varphi(p^{k-1})=p^{k-2}*(p-1)\)

\(\therefore p^{k-1}=\varphi(p^{k-1})*p/(p-1)\)

\(\therefore \varphi(p^k)=\varphi(p^{k-1})*p\)

线性筛欧拉函数:

做法:在线性筛的基础上加上,若\(i\)为质数,则\(\varphi(i)=i-1\),之后根据他是积性函数的定义,则对于的\(i \mod p_j\not=0(p为小于i的质数集和)\),都有\(\varphi(i*p_j)=\varphi(i)*\varphi(p_j)\),若是\(i \mod p_j=0\),则我们将\(i*p_j\)写成互质的形式,\(i*p_j=t*p_j^w\),所以\(\varphi(i*p_j)=\varphi(t*p_j^w)=\varphi(t)*\varphi(p_j^{w-1})*p_j=\)\(\varphi(i)*p_j\),所以做完。

代码实现:

#include<map>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 20000000
#define int long long
using namespace std;
void read(int &sum)
{
	sum=0;char last='w',ch=getchar();
	while (ch<'0' || ch>'9') last=ch,ch=getchar();
	while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
	if (last=='-') sum=-sum;
}
int t,n;
int ol[maxn+5];
int p[maxn+5],bz[maxn+5],len;
void find_prime()
{
	ol[1]=1;
	for (int i=2;i<=maxn;i++)
	{
		if (bz[i]==false) ol[i]=i-1,len++,p[len]=i;
		for (int j=1;j<=len && i*p[j]<=maxn;j++)
		{
			bz[i*p[j]]=1;
			if (i%p[j]==0) { ol[i*p[j]]=ol[i]*p[j]; break; }
			else ol[i*p[j]]=ol[i]*ol[p[j]];
		}
	}
}
signed main()
{
//	freopen("M.in","r",stdin);
//	freopen("M.out","w",stdout);
	find_prime();
//	fclose(stdin);fclose(stdout);
	return 0;
}

莫比乌斯函数与欧拉函数加回来。同时你又发现,把\(d\)质因数分解,\(d=p_1^{\alpha_1}*p_2^{\alpha_2}*...*p_r^{\alpha_r}\)。若\(\alpha_i\),则用\(d\)减去的数肯定就会被它的因数减过,并被刚好减一次(自己仔细想想)。所以我们也可以忽略这种情况,不加也不减。所以你可以发现:这其实就是一个容斥原理:\(\varphi(n)=\sum_{d|n}(-1)^r*\frac{n}{d}\),所以:\(\varphi(n)=\sum_{d|n}\mu(d)id(\frac{n}{d})\)


杜教筛基本套路

作用:用来求积性函数\(f(n)\)的前缀和\(F(n)=\sum_{i=1}^{n}f(n)\)

过程:

1. 当看到这种题的时候,D老师想到了用狄利克雷卷积来解决,所以他g构造两个积性函数\(h,g\),使得\(h=f*g\)
2. \(\ \because \sum_{i=1}^{n}h(i)=\sum_{i=1}^{n}\sum_{d|i}f(d)g(\frac{i}{d})\) (由狄利克雷卷积的定义可得)
3. \(\ \therefore \sum_{i=1}^{n}h(i)=\sum_{i=1}^{n}[g(d)*\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}f(i)]\) (把上式拆解开来,我们可以得到\(f(d_{1,1})*g(\frac{i_1}{d_{1,1}})+f(d_{1,2})*g(\frac{i_1}{d_{1,2}})+...+f(d_{1,t_i})*g(\frac{i_1}{d_{1,t_i}})+...+\)\(f(d_{n,1})*g(\frac{i_n}{d_{n,1}})\)\(+...+f(d_{n,t_n})*g(\frac{i_n}{d_{n,t_n}})\),之后我们就可以知道\(\sum_{d=1}^{n} g(d)\)都出现了,那么每个\(g(d)\)出现了多少次呢?经过观察我们发现,对于每个\(g(d)\)它在\(1\)~\(n\)范围内,只要是\(d\)的倍数的数,就都会含有\(g(d)\),所以每个\(g(d)\)总共出现了\(\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}f(i)\)次)
4. \(\ \therefore \sum_{i=1}^{n}h(i)=\sum_{i=1}^{n}[g(d)*F(\lfloor \frac{n}{d} \rfloor)]\) (根据\(F(n)\)的定义,直接代入)
5. \(\ \therefore \sum_{i=1}^{n}h(i)=g(1)F(n)+\sum_{i=2}^{n}[g(d)*F(\lfloor \frac{n}{d} \rfloor)]\) (将\(i=1\)提取出来,因为\(d=1\),所以\(\lfloor \frac{n}{d} \rfloor=n\))
6. \(\therefore g(1)F(n)=\sum_{i=1}^{n}h(i)-\sum_{i=2}^{n}[g(d)*F(\lfloor \frac{n}{d} \rfloor)]\) (移项易得)
7. 找得一个\(g(n)\)使得\(\sum_{i=1}^{n}h(i)\)能在短时间内求出,那么我们就可以分块求出\(-\sum_{i=2}^{n}[g(d)*F(\lfloor \frac{n}{d} \rfloor)]\),总体时间复杂度在\(n^{\frac{3}{4}}\)左右

题目:

莫比乌斯之和(题面

解析:根据杜教筛套路式,我们容易得出\(I(n)\)就是可以让我们快速求出的积性函数,那么\(F(n)=\sum_{i=1}^{n}g(i)*f(i)-\sum_{i=2}^{n}[g(d)*F(\lfloor \frac{n}{d} \rfloor)]\),又因为\(\mu*I=\varepsilon\),所以\(F(n)=1-\sum_{i=2}^{n}[I(d)*F(\lfloor \frac{n}{d} \rfloor)]\),之后分块求解即可。

代码实现:

#include<bits/stdc++.h>
#define MAXN 5000000
#define mod 2333333
#define ll long long
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}

struct my_map{
    ll x;int ans,next;
}e[MAXN+5];
int f[MAXN+5],ans=0,num=0,s[MAXN],head[mod+5];
bool b[MAXN+5];

void ins(ll x,int sum)
{
    int j=x%mod;
    e[++num]=(my_map){x,sum,head[j]};
    head[j]=num;
}

int calc(ll n)
{
    if(n<=MAXN) return f[n];
    for(int i=head[n%mod];i;i=e[i].next)
        if(e[i].x==n)return e[i].ans;
    int sum=1,q=sqrt(n);
    for(int i=2;i<=q;i++)
        sum-=calc(n/i);
    q=n/(q+1);
    for(int i=1;i<=q;i++)
        sum-=(n/i-(n/(i+1)))*calc(i);
    ins(n,sum);
    return sum;
}

int main()
{
    f[1]=1;b[1]=1;
    for(int i=2;i<=MAXN;i++)
    {
        if(!b[i]) s[++num]=i,f[i]=-1;
        for(int j=1;j<=num&&s[j]*i<=MAXN;j++)
        {
            int t=s[j]*i; b[t]=1;
            if(i%s[j]==0){f[t]=0;break;}
            f[t]=-f[i];
        }
    }
    for(int i=2;i<=MAXN;i++)
        f[i]+=f[i-1];
    num=0;
    ll x=read();ans-=calc(x-1);
    x=read();ans+=calc(x);
    cout<<ans;
    return 0;
}

欧拉函数与莫比乌斯函数前缀和(题面

解析:关于莫比乌斯的跟上题一样,而欧拉函数有\(\varphi*I=id\),所以\(g=I\),所以\(F(n)=\sum_{i=1}^{n}i-\sum_{i=2}^{n}F(\lfloor \frac{n}{d} \rfloor)\),注意可以吧\(\lfloor \frac{n}{d} \rfloor\)相同的合并来算,减小时间,详见代码。

代码实现:

#include<map>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 20000000
#define int long long
using namespace std;
map<int,int> find_mu;
map<int,int> find_ol;
void read(int &sum)
{
	sum=0;char last='w',ch=getchar();
	while (ch<'0' || ch>'9') last=ch,ch=getchar();
	while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
	if (last=='-') sum=-sum;
}
int t,n;
int ol[maxn+5],mu[maxn+5];
int p[maxn+5],bz[maxn+5],len;
void find_prime()
{
	mu[1]=ol[1]=1;
	for (int i=2;i<=maxn;i++)
	{
		if (bz[i]==false) mu[i]=-1,ol[i]=i-1,len++,p[len]=i;
		for (int j=1;j<=len && i*p[j]<=maxn;j++)
		{
			bz[i*p[j]]=1;
			if (i%p[j]==0) { mu[i*p[j]]=0,ol[i*p[j]]=ol[i]*p[j]; break; }
			else mu[i*p[j]]=-mu[i],ol[i*p[j]]=ol[i]*ol[p[j]];
		}
	}
	for (int i=1;i<=maxn-1;i++) mu[i+1]+=mu[i];
	for (int i=1;i<=maxn-1;i++) ol[i+1]+=ol[i];
}
int dfs1(int x)
{
	if (x<=maxn) return ol[x];
	if (find_ol[x]!=0) return find_ol[x];
	int sum=(1+x)*x/2;
	for (int l=2,r;l<=x;l=r+1)
	{
		r=x/(x/l);
		sum-=(r-l+1)*dfs1(x/l);
	}
	return find_ol[x]=sum;
}
int dfs2(int x)
{
	if (x<=maxn) return mu[x];
	if (find_mu[x]!=0) return find_mu[x];
	int sum=1;
	for (int l=2,r;l<=x;l=r+1)
	{
		r=x/(x/l);
		sum-=(r-l+1)*dfs2(x/l);
	}
	return find_mu[x]=sum;
}
signed main()
{
//	freopen("M.in","r",stdin);
//	freopen("M.out","w",stdout);
	find_prime();
	read(t);
	while (t--)
	{
		read(n);
		printf("%lld %lld\n",dfs1(n),dfs2(n));
	}
//	fclose(stdin);fclose(stdout);
	return 0;
}

欧拉函数前缀和(题面

解析:同上,用了乘法逆元

代码实现:

#include<map>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn = 5e6+5;
#define int long long
#define mod 1000000007
using namespace std;
typedef long long ll;
map<int,int> _ol;
int ol[maxn+5];
int p[maxn+5],len,bz[maxn+5];
int n;
void read(int &sum)
{
	sum=0;char last='w',ch=getchar();
	while (ch<'0' || ch>'9') last=ch,ch=getchar();
	while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
	if (last=='-') sum=-sum;
}
void find_prime()
{
	ol[1]=1;
	for (int i=2;i<=maxn;i++)
	{
		if (bz[i]==false) ol[i]=i-1,p[++len]=i;
		for (int j=1;j<=len && i*p[j]<=maxn;j++)
		{	
			bz[i*p[j]]=true;
			if (i%p[j]==0) { ol[i*p[j]]=ol[i]*p[j]%mod; break; }
			else ol[i*p[j]]=ol[i]*ol[p[j]]%mod;
		}
	}
	for (int i=2;i<=maxn;i++)
		ol[i]+=ol[i-1],ol[i]%=mod;
}
int find_ol(int x)
{
	if (x<=maxn) return ol[x];
	if (_ol[x]!=0) return _ol[x];
	int sum=(1+x)*x%mod*500000004%mod;
	while (sum<0) sum+=mod,sum%=mod;
	for (int l=2,r;l<=x;l=r+1)
	{
		r=x/(x/l);
		sum-=(r-l+1)*find_ol(x/l);
		while (sum<0) sum+=mod,sum%=mod;
		sum%=mod;
	}
	while (sum<0) sum+=mod,sum%=mod;
	if (_ol[x]==0) _ol[x]=sum%mod;
	return sum%mod;
}
signed main()
{
//	freopen("M.in","r",stdin);
//	freopen("M.out","w",stdout);
	find_prime();
	read(n);
	int t=find_ol(n);
	while (t<0) t+=mod,t%=mod;
	printf("%lld\n",t);
//	fclose(stdin);fclose(stdout);
	return 0;
}

posted @ 2021-10-17 10:18  WBWYX  阅读(240)  评论(0)    收藏  举报