狄利克雷卷积

文中 * 表示卷积 \cdot 表示乘积
就是有两个积性函数 g g g f f f
g f = <munder> d n </munder> g ( n ) f ( n d ) g*f=\sum_{d|n}g(n)\cdot f(\frac{n}{d}) gf=dng(n)f(dn)
这个就叫做狄利克雷卷积

一些常见的积性数论函数

①单位元: e ( n ) = [ n = = 1 ] e(n)=[n==1] e(n)=[n==1]
就是说只有当n等于1的时候等于1,其他时候都等于0,这个中括号也是常见的表达方式,就是说只有当中括号里面的东西为真的时候是1
结论: e = μ 1 e=\mu*1 e=μ1
证明看这里:https://blog.csdn.net/swustlpf/article/details/78634667
②常值函数: 1 ( n ) = 1 1(n)=1 1(n)=1
③单位函数: I d ( n ) = n Id(n)=n Id(n)=n
结论: I d = φ 1 Id=\varphi*1 Id=φ1
证明看这里:https://blog.csdn.net/alan_cty/article/details/50696793#comments
④:欧拉函数: φ ( n ) \varphi(n) φ(n)
⑤:莫比乌斯函数: μ ( n ) \mu(n) μ(n)

杜教筛

假如要求积性函数 f f f 的前 n n n 项和
就要找一个积性函数 g g g 使得 i = 1 n g f \sum_{i=1}^ng*f i=1ngf 是个很好求得的
也就是说假如 h = g f h=g*f h=gf,那么 i = 1 n h ( i ) \sum_{i=1}^nh(i) i=1nh(i)这个是非常好求的,并且假设求出来就等于 A A A AAA AAA

然后就是杜教筛的正文了:

就是对等式的右边进行变换:
<munderover> i = 1 n </munderover> <munder> d i </munder> g ( d ) f ( i d ) = <munderover> i = 1 n </munderover> g ( i ) <munderover> j = 1 n i </munderover> f ( j ) \sum_{i=1}^n\sum_{d|i}g(d)\cdot f(\frac{i}{d})=\sum_{i=1}^ng(i)\cdot \sum_{j=1}^{\frac{n}{i}}f(j) i=1ndig(d)f(di)=i=1ng(i)j=1inf(j)

感觉这一步就是最关键的了,我看了好久都不是很懂,然后打了个表来验证
哎呀我懂了数论题中交换求和符号,快夸奖我٩(๑>◡<๑)۶

左边的公式感觉就是把每个数拆成他有的两个因子在那里乘,右边的公式感觉就是收集一下每个因子乘了哪些数
哇我真的是反应不过来这个啊T_T

然后后面的还好看懂,用 S ( n ) S(n) S(n) 表示 f f f 的前 n n n 项和:
= <munderover> i = 1 n </munderover> g ( i ) S ( n i ) =\sum_{i=1}^n g(i)\cdot S(\frac{n}{i}) =i=1ng(i)S(in)
然后再把 i = 1 i=1 i=1 这一项单独拿出来:
= g ( 1 ) S ( n ) + <munderover> i = 2 n </munderover> g ( i ) S ( n i ) =g(1)\cdot S(n)+\sum_{i=2}^n g(i)\cdot S(\frac{n}{i}) =g(1)S(n)+i=2ng(i)S(in)

这样 S ( n ) S(n) S(n) 的关系式就有了,只不过前面有个系数 g ( 1 ) g(1) g(1)

再把左边那好算的一坨 i = 1 n g f \sum_{i=1}^ng*f i=1ngf 写回来,假设他算出来等于 A A A AAA AAA
因此就是:
A A A = g ( 1 ) S ( n ) + <munderover> i = 2 n </munderover> g ( i ) S ( n i ) AAA=g(1)\cdot S(n)+\sum_{i=2}^n g(i)\cdot S(\frac{n}{i}) AAA=g(1)S(n)+i=2ng(i)S(in)
所以最终 S ( n ) S(n) S(n) 就是:
S ( n ) = 1 g ( 1 ) ( A A A <munderover> i = 2 n </munderover> g ( i ) S ( n i ) ) S(n)=\frac{1}{g(1)}\cdot (AAA-\sum_{i=2}^n g(i)\cdot S(\frac{n}{i})) S(n)=g(1)1(AAAi=2ng(i)S(in))
然后如果 g ( i ) g(i) g(i) 是个常数,那么计算 S ( n i ) S(\frac{n}{i}) S(in) 的时候要 数论分块 加速一哈
就是说 n i \frac{n}{i} in 这个值在 [ i , n n / i ] [i,\frac{n}{n/i}] [i,n/in] 这段区间内都是一样的

#51nod 1239 欧拉函数前缀和
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1239
好现在来套公式做题练练手:
首先要找一个合适的 g g g 函数,来和轻易地求出 A A A AAA AAA
对于欧拉函数,找的是 1 1 1 函数 1 ( n ) 1(n) 1(n)
不懂怎么找到的,难道是凑的???
因为 φ 1 = i d \varphi*1=id φ1=id
也就是说 d n φ ( d ) 1 ( n d ) = n \sum_{d|n}\varphi(d)\cdot 1(\frac{n}{d})=n dnφ(d)1(dn)=n
也就是那个结论: <munder> d n </munder> φ ( d ) = n \sum_{d|n}\varphi(d)=n dnφ(d)=n
证明阔以看这位童鞋滴,他的第二种比较好理解
那我们的 A A A = g f = i = 1 n d i φ ( d ) = i = 1 n i = n ( n + 1 ) 2 AAA=\sum g*f=\sum_{i=1}^n\sum_{d|i}\varphi(d)=\sum_{i=1}^ni=\frac{n(n+1)}{2} AAA=gf=i=1ndiφ(d)=i=1ni=2n(n+1)
所以我们这里的 A A A AAA AAA 就是 n ( n + 1 ) 2 \frac{n(n+1)}{2} 2n(n+1)

再来化简右边,把 g g g 函数换成 1 1 1 函数:
<munderover> i = 1 n </munderover> <munder> d i </munder> 1 ( n ) φ ( i d ) \sum_{i=1}^n\sum_{d|i}1(n)\cdot \varphi(\frac{i}{d}) i=1ndi1(n)φ(di)
= <munderover> i = 1 n </munderover> 1 ( i ) <munderover> j = 1 n i </munderover> φ ( j ) =\sum_{i=1}^n1(i)\cdot \sum_{j=1}^{\frac{n}{i}}\varphi(j) =i=1n1(i)j=1inφ(j)
再把 i = 1 i=1 i=1 这一项单独弄出来
= 1 ( 1 ) S ( n ) + <munderover> i = 2 n </munderover> 1 ( i ) S ( n i ) =1(1)\cdot S(n)+\sum_{i=2}^n 1(i)\cdot S(\frac{n}{i}) =1(1)S(n)+i=2n1(i)S(in)
1 1 1 函数是常函数一直等于1 ,就简化一下,最终就推出这个公式,左边=右边:
n ( n + 1 ) 2 = S ( n ) + <munderover> i = 2 n </munderover> S ( n i ) \frac{n(n+1)}{2}=S(n)+\sum_{i=2}^nS(\frac{n}{i}) 2n(n+1)=S(n)+i=2nS(in)
所以最终就是:
S ( n ) = n ( n + 1 ) 2 <munderover> i = 2 n </munderover> S ( n i ) S(n)=\frac{n(n+1)}{2}-\sum_{i=2}^nS(\frac{n}{i}) S(n)=2n(n+1)i=2nS(in)
而这种就是属于阔以分块加速的~

#include"bits/stdc++.h"
using namespace std;
const int maxn=1e6+5;
const int MOD=1e9+7;
const int inv2=5e8+4;
typedef long long LL;
bool vis[maxn];
LL phi[maxn],Sphi[maxn];//Å·À­º¯Êý£¬Å·À­º¯Êýǰ׺ºÍ 
vector<int>prime;
map<LL,LL>Mp;
void PHI(int n)
{
	memset(vis,1,sizeof(vis));
	phi[1]=1;
	Sphi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(vis[i])
		{
			prime.push_back(i);
			phi[i]=i-1;
		}
		for(int j=0;j<prime.size()&&i*prime[j]<=n;j++)
		{
			vis[i*prime[j]]=0;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
		Sphi[i]=(Sphi[i-1]+phi[i])%MOD;
	}
}
LL S(LL n)
{
	if(n<=maxn-5)return Sphi[n];
	if(Mp[n])return Mp[n];
	LL res=(n%MOD)*((n+1)%MOD)%MOD*inv2%MOD;
	for(LL i=2,j;i<=n;i=j+1)
	{
		j=n/(n/i);
		res-=(LL)(j-i+1)%MOD*S(n/i)%MOD;
		res%=MOD;
	}
	res=(res+MOD)%MOD;
	return Mp[n]=res;
}
int main()
{
	PHI(maxn-5);
	LL N;
	while(cin>>N)
	{
		cout<<S(N)<<endl;
	}
}

#51nod 1244 莫比乌斯函数前缀和
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1244
这次选的 g g g 函数也是 1 1 1 函数
使用的性质是: μ 1 = e \mu*1=e μ1=e
证明阔以看这里
也就是 d n μ ( d ) 1 ( n d ) = d n μ ( d ) = e \sum_{d|n}\mu(d)\cdot 1(\frac{n}{d})=\sum_{d|n}\mu(d)=e dnμ(d)1(dn)=dnμ(d)=e
这样子计算出的 A A A = i = 1 n d i μ ( d ) = 1 AAA=\sum_{i=1}^n\sum_{d|i}\mu(d)=1 AAA=i=1ndiμ(d)=1

那直接套公式阔以得到: S ( n ) = 1 <munderover> i = 2 n </munderover> S ( n i ) S(n)=1-\sum_{i=2}^nS(\frac{n}{i}) S(n)=1i=2nS(in)

#51nod 1237 最大公约数之和 V3
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1237

f ( d ) f(d) f(d) 表示 g c d ( i , j ) = d gcd(i,j)=d gcd(i,j)=d 的个数
那么其实就是求:
<munderover> d = 1 n </munderover> d f ( d ) \sum_{d=1}^nd \cdot f(d) d=1ndf(d)
而怎么求 f ( d ) f(d) f(d) 喃?很经典的一个就是用莫比乌斯反演,但是这样是 O ( n ) O(n) O(n)
还有一个公式就是 2 <munderover> i = 1 n </munderover> φ ( i ) 1 2\sum_{i=1}^n \varphi (i)-1 2i=1nφ(i)1
为什么喃?
<munderover> i = 1 n </munderover> <munderover> j = 1 i </munderover> [ g c d ( i , j ) = = 1 ] = φ ( i ) \sum_{i=1}^n\sum_{j=1}^i[gcd(i,j)==1]=\varphi(i) i=1nj=1i[gcd(i,j)==1]=φ(i)
相当于枚举 i i i 然后 j j j 是小于 i i i 中与 i i i 互质的,那么这不就是 φ ( i ) \varphi(i) φ(i) 的个数么?
然后就是 j > = i j>=i j>=i 的时候,相当于枚举 j j j ,因此是两倍,最后减个 1 1 1 是因为 ( 1 , 1 ) (1,1) (1,1) 这个是计算进了欧拉函数中的,算了两次,所以要减去1

而我们要的是 g c d ( i , j ) = d gcd(i,j)=d gcd(i,j)=d 的,这样处理一哈就行了:
<munderover> i = 1 n </munderover> <munderover> j = 1 n </munderover> [ g c d ( i , j ) = = d ] = <munderover> i = 1 n d </munderover> <munderover> j = 1 n d </munderover> [ g c d ( i , j ) = = 1 ] = 2 <munderover> i = 1 n d </munderover> φ ( i ) 1 = 2 S ( n d ) 1 \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==d]=\sum_{i=1}^\frac{n}{d}\sum_{j=1}^\frac{n}{d}[gcd(i,j)==1]=2\sum_{i=1}^\frac{n}{d}\varphi(i)-1=2S(\frac{n}{d})-1 i=1nj=1n[gcd(i,j)==d]=i=1dnj=1dn[gcd(i,j)==1]=2i=1dnφ(i)1=2S(dn)1
于是公式就出来了:
a n s = <munderover> d = 1 n </munderover> d ( 2 S ( n d ) 1 ) ans=\sum_{d=1}^nd\cdot(2S(\frac{n}{d})-1) ans=d=1nd(2S(dn)1)
这样看起来好像也是 O ( n ) O(n) O(n) 的, d d d 要遍历 1 1 1 n n n,但是我们阔以数论分块~~~
[ d , n / ( n / d ) ] [d,n/(n/d)] [d,n/(n/d)] 这一段内的 S ( n d ) S(\frac{n}{d}) S(dn) 是相同的,而这一段的 d d d 也只是个等差数列非常好求,因此,就阔以写了~

#include"bits/stdc++.h"
using namespace std;
const int maxn=1e6+5;
const int MOD=1e9+7;
const int inv2=5e8+4;
typedef long long LL;
bool vis[maxn];
LL phi[maxn],Sphi[maxn];//Å·À­º¯Êý£¬Å·À­º¯Êýǰ׺ºÍ 
vector<int>prime;
map<LL,LL>Mp;
void PHI(int n)
{
	memset(vis,1,sizeof(vis));
	phi[1]=1;
	Sphi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(vis[i])
		{
			prime.push_back(i);
			phi[i]=i-1;
		}
		for(int j=0;j<prime.size()&&i*prime[j]<=n;j++)
		{
			vis[i*prime[j]]=0;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
		Sphi[i]=(Sphi[i-1]+phi[i])%MOD;
	}
}
LL S(LL n)
{
	if(n<=maxn-5)return Sphi[n];
	if(Mp[n])return Mp[n];
	LL res=(n%MOD)*((n+1)%MOD)%MOD*inv2%MOD;
	for(LL i=2,j;i<=n;i=j+1)
	{
		j=n/(n/i);
		res-=(LL)(j-i+1)%MOD*S(n/i)%MOD;
		res%=MOD;
	}
	res=(res+MOD)%MOD;
	return Mp[n]=res;
}
int main()
{
	PHI(maxn-5);
	LL N;
	while(cin>>N)
	{
		LL res=0;
		for(LL d=1,j;d<=N;d=j+1)
		{
			j=N/(N/d);
			LL t1=(d+j)%MOD;
			LL t2=(j-d+1)%MOD;
			LL t=t1*t2%MOD*inv2%MOD;
			res+=t%MOD*(2LL*S(N/d)-1)%MOD;
			res%=MOD;
		}
		res=(res+MOD)%MOD;
		cout<<res<<endl;
	}
}

#51nod 1188 最大公约数之和 V2
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1188

还是从最古老的公式入手,就是想求一个数m与n以内的数互质的个数: <munderover> i = 1 m </munderover> [ g c d ( i , m ) = = 1 ] = <munder> d m </munder> μ ( d ) n d \sum_{i=1}^m[gcd(i,m)==1]=\sum_{d|m}\mu(d)\cdot\frac{n}{d} i=1m[gcd(i,m)==1]=dmμ(d)dn
m = n m=n m=n 时,很明显就是欧拉函数的定义,而且也退化成了一个常见的狄利克雷卷积的结论:
φ = μ i d \varphi=\mu*id φ=μid

这是求的 g c d = 1 gcd=1 gcd=1 的方法,要是求的 g c d = d gcd=d gcd=d怎么办喃,就是同时除以 d d d 就转换成了 g c d = 1 gcd=1 gcd=1 的了,那么 n d \frac{n}{d} dn 内互质的个数就是 φ ( n d ) \varphi(\frac{n}{d}) φ(dn)

然后就是枚举因子 d d d 的时候只用枚举到 s q r t ( n ) sqrt(n) sqrt(n),,因为如果 d d d 是n的因子,那么 n d \frac{n}{d} dn 肯定也是 n的因子,这样就不会超时了,这个很关键

因为做V2的时候脑子有点反应不过来,因此在V1这里先做个铺垫,就是求V1,如果数据范围改成1e6的话,就这个阔以改成筛法,感觉线性筛就有点考脑子了。

筛法只能过1e6的数据

#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=1e6+5;
const int MOD=1e9+7;
LL N,M;
vector<int>prime;
int phi[maxn];
bool vis[maxn];
map<LL,LL>Mp;
LL ans[maxn];
void PHI(int n)
{
	memset(vis,1,sizeof(vis));
	phi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(vis[i])
		{
			prime.push_back(i);
			phi[i]=i-1;
		}
		for(int j=0;j<prime.size()&&i*prime[j]<=n;j++)
		{
			vis[i*prime[j]]=0;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}
int main()
{
	PHI(maxn-5);
	for(LL d=1;d<=maxn-5;d++)//ö¾ÙÒò×Ó 
	{
		for(LL i=1;d*i<=maxn-5;i++)
		{
			LL n=d*i;
			ans[n]+=d*phi[i];//Ï൱ÓÚÊǶÔn=d*iÕâ¸öÊýµÄ¹±Ï× 
		}
	}
	int N;
	while(cin>>N)
	{
		cout<<ans[N]<<endl;
	 } 
}

理解了这个的话V2就好做了