莫比乌斯反演专题(基础篇)

0:前置知识

线性筛积性函数

int mu[N],prime[N],tot=0;
int v[N];
void sieve()
{
	mu[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!v[i])
		{
			v[i]=i;
			prime[++tot]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=tot;j++)
		{
			if(prime[j]>v[i]||i*prime[j]>n) break;
			v[i*prime[j]]=prime[j];
			if(i%prime[j]==0)
			{
				mu[i*prime[j]]=0;
				break;
			}
			else mu[i*prime[j]]=mu[i]*mu[prime[j]];
		}
	}
}

整除分块

	int l,r;
	int ans=0;
	for(;l<=r;l=r+1)
	{
		r=(n/(n/l));
		ans+=(r-l+1)*(n/l);
	}

莫比乌斯函数

μ ( n ) \mu (n) μ(n) n n n的莫比乌斯函数,其中 n = Π p i c i n=\Pi_{p_i}^{c_i} n=Πpici
则若 c i c_i ci不等于1, μ ( n ) = 0 \mu(n)=0 μ(n)=0
p i p_i pi的个数为 k k k,则 μ ( n ) = ( − 1 ) k \mu(n)=(-1)^k μ(n)=(1)k
特殊的, μ ( 1 ) = 1 \mu(1)=1 μ(1)=1
性质: ∑ d ∣ n μ ( d ) = [ n = = 1 ] \sum_{d|n}\mu(d)=[n==1] dnμ(d)=[n==1]
μ ( n ) = − ∑ d ∣ n , d ! = n μ ( d ) ) \mu(n)=-\sum_{d|n ,d!=n}\mu(d)) μ(n)=dn,d!=nμ(d))
μ ( n ) = μ ( a ) μ ( b ) \mu(n)=\mu(a)\mu(b) μ(n)=μ(a)μ(b),其中 n = a × b n=a\times b n=a×b,且 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1

狄雷克雷卷积

h ( n ) = ∑ d ∣ n f ( n ) g ( n / d ) h(n)=\sum _{d|n}f(n)g(n/d) h(n)=dnf(n)g(n/d)
h h h g g g f f f的狄利克雷卷积,记作 h = f × g h=f\times g h=f×g
e ( n ) e(n) e(n)为元函数,表示 e ( n ) = [ n = = 1 ] e(n)=[n==1] e(n)=[n==1]
i d ( n ) id(n) id(n)为单位函数,表示 i d ( n ) = n id(n)=n id(n)=n
I ( n ) I(n) I(n)为恒等函数,表示 I ( n ) = 1 I(n)=1 I(n)=1
几个性质
1: f × g = g × f f\times g=g\times f f×g=g×f:交换律
2: f × g × h = f × ( g × h ) f\times g\times h=f\times (g\times h) f×g×h=f×(g×h)
3: f × e = f f\times e=f f×e=f:因为如果 n / d n/d n/d不等于1,式子为0,而此时式子的值为 f ( n ) f(n) f(n)
4: μ × I = e \mu \times I=e μ×I=e ∑ d ∣ n μ ( d ) = [ n = = 1 ] \sum_{d|n}\mu(d)=[n==1] dnμ(d)=[n==1]
5: φ × I = i d \varphi \times I= id φ×I=id ∑ d ∣ n φ ( d ) = n \sum_{d|n}\varphi(d)=n dnφ(d)=n
6: φ = μ × i d \varphi=\mu\times id φ=μ×id
证明:将5式的两边同时卷上 μ \mu μ,由之前的性质可得, φ = μ × i d \varphi =\mu \times id φ=μ×id

莫比乌斯反演

反演公式:记 f ( n ) = ∑ d ∣ n g ( n ) f(n)=\sum_{d|n}g(n) f(n)=dng(n)
g ( n ) = ∑ d ∣ n f ( d ) μ ( n / d ) g(n)=\sum_{d|n}f(d)\mu(n/d) g(n)=dnf(d)μ(n/d)
原理: f = g × I f = g\times I f=g×I
f × μ = g × e f\times \mu=g \times e f×μ=g×e
g = f × μ g=f\times \mu g=f×μ

例题

1:周期性的字符串

对于长度为n、由小写字母组成的(不一定要用所有字母)字符串s,如果存在一个字符串t,t≠s,使得s可以由t重复若干次恰好得到(不能有多余字符),那么我们称s具有周期性。

周期性字符串计数问题是指对于给定的n,求有多少个长度为n的周期性字符串。答案比较大,只需要输出对 1 0 9 + 7 10^9+7 109+7取余即可。
注意串中只包含26个小写字母。

分析

f ( n ) f(n) f(n)表示长度为 n n n的字符串个数之和,则 f ( n ) = 2 6 n f(n)=26^n f(n)=26n
g ( n ) g(n) g(n)表示长度为 n n n,且没有周期性为(或者说周期为1)的字符串个数
则长度为n的字符串可以看成长度为d的字符串重复出现 n / d n/d n/d次形成的
f ( n ) = ∑ d ∣ n g ( n ) f(n)=\sum_{d|n}g(n) f(n)=dng(n)
由莫比乌斯反演公式可知 g ( n ) = ∑ d ∣ n f ( d ) μ ( n / d ) g(n)=\sum_{d|n}f(d)\mu(n/d) g(n)=dnf(d)μ(n/d)
我们求出 g ( n ) g(n) g(n),则题目所求的就是周期不为1的字符串个数,我们用总的字符串数量减掉周期为1,长度为n的字符串就是题目所求
也就是 f ( n ) − g ( n ) f(n)-g(n) f(n)g(n)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9+7;
const int N = 1e6+7;
int mu[N],prime[N],tot=0;
int v[N];
LL Pow[N];
void init(int n)
{
	mu[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!v[i])
		{
			v[i]=i;
			prime[++tot]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=tot;j++)
		{
			if(prime[j]>v[i]||i*prime[j]>n) break;
			v[i*prime[j]]=prime[j];
			if(i%prime[j]==0)
			{
				mu[i*prime[j]]=0;
				break;
			}
			else mu[i*prime[j]]=mu[i]*mu[prime[j]];
		}
	}
	Pow[0]=1;
	for(int i=1;i<=n;i++)
	Pow[i]=1ll*Pow[i-1]*26%mod;
}
LL f(int n)
{
	return Pow[n];
}
LL g(int n)
{
	LL ans=0;
	for(int d=1;d<=n;d++)
	if(n%d==0) ans=(ans+f(d)*mu[n/d]%mod)%mod;
	return ans;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	LL n;
	cin>>n;
	init(n);
	cout<<((f(n)-g(n))%mod+mod)%mod;
	return 0;
}
2:互质数对

∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = = 1 ] \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1] i=1nj=1n[gcd(i,j)==1]
因为 ∑ d ∣ n μ ( d ) = [ n = = 1 ] \sum_{d|n}\mu(d)=[n==1] dnμ(d)=[n==1]
n n n换成 g c d ( i , j ) gcd(i,j) gcd(i,j)可知,所求为 ∑ i = 1 n ∑ j = 1 n ∑ d ∣ i , d ∣ j μ ( d ) \sum_{i=1}^n\sum_{j=1}^n\sum_{d|i,d|j}\mu(d) i=1nj=1ndi,djμ(d)
d d d提前, ∑ d = 1 n μ ( d ) ∑ d ∣ i ∑ d ∣ j 1 \sum_{d=1}^n\mu(d)\sum_{d|i}\sum_{d|j}1 d=1nμ(d)didj1
i , j i,j i,j换成倍数可得 ∑ d = 1 n μ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ \sum_{d=1}^n\mu(d)\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{d} \rfloor} d=1nμ(d)i=1dnj=1dn
= ∑ d = 1 n μ ( d ) ⌊ n d ⌋ ⌊ n d ⌋ =\sum_{d=1}^n\mu(d)\lfloor \frac{n}{d} \rfloor \lfloor \frac{n}{d} \rfloor =d=1nμ(d)dndn

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7+7;
LL mu[N];
int prime[N],tot=0;
int v[N];
void init(int n)
{
	mu[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!v[i])
		{
			v[i]=i;
			prime[++tot]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=tot;j++)
		{
			if(prime[j]>v[i]||i*prime[j]>n) break;
			v[i*prime[j]]=prime[j];
			if(i%prime[j]==0)
			{
				mu[i*prime[j]]=0;
				break;
			}
			else mu[i*prime[j]]=mu[i]*mu[prime[j]];
		}
	}
	for(int i=1;i<=n;i++)
	mu[i]=mu[i-1]+mu[i];
}
int main()
{
	freopen("mu.in","r",stdin);
	freopen("mu.out","w",stdout);
	int n;
	cin>>n;
	init(n);
	LL ans=0;
	int l=1,r;
	for(;l<=n;l=r+1)
	{
		r=(n/(n/l));
		ans=(ans+(mu[r]-mu[l-1])*(n/l)*(n/l));
	}
	cout<<ans;
	return 0;
}
【BZOJ2693】jzptab

∑ i = 1 n ∑ j = 1 m l c m ( i , j ) \sum_{i=1}^n\sum_{j=1}^mlcm(i,j) i=1nj=1mlcm(i,j)

思路

S ( n ) = ∑ i = 1 n i S(n)=\sum_{i=1}^ni S(n)=i=1ni
∑ i = 1 n ∑ j = 1 m l c m ( i , j ) = ∑ i = 1 n ∑ j = 1 m i j g c d ( i , j ) \sum_{i=1}^n\sum_{j=1}^mlcm(i,j)=\sum_{i=1}^n\sum_{j=1}^m\frac{ij}{gcd(i,j)} i=1nj=1mlcm(i,j)=i=1nj=1mgcd(i,j)ij
= ∑ i = 1 n ∑ j = 1 m ∑ d ∣ i , d ∣ j [ g c d ( i , j ) = = d ] i j d =\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}[gcd(i,j)==d]\frac{ij}{d} =i=1nj=1mdi,dj[gcd(i,j)==d]dij
= ∑ d = 1 n ∑ d ∣ i ∑ d ∣ j [ g c d ( i , j ) = = d ] i j d = \sum_{d=1}^n\sum_{d|i}\sum_{d|j}[gcd(i,j)==d]\frac{ij}{d} =d=1ndidj[gcd(i,j)==d]dij
= ∑ d = 1 n ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = = 1 ] i j d 2 d = \sum_{d=1}^n\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}[gcd(i,j)==1]\frac{ijd^2}{d} =d=1ni=1dnj=1dm[gcd(i,j)==1]dijd2
= ∑ d = 1 n d ∑ i = 1 ⌊ n d ⌋ i ∑ j = 1 ⌊ m d ⌋ j ∑ k ∣ i , k ∣ j μ ( k ) = \sum_{d=1}^nd\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}i\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}j\sum_{k|i,k|j}\mu(k) =d=1ndi=1dnij=1dmjki,kjμ(k)
= ∑ d = 1 n d ∑ k = 1 ⌊ n d ⌋ μ ( k ) ∑ k ∣ i ⌊ n d ⌋ i ∑ k ∣ j ⌊ m d ⌋ j = \sum_{d=1}^nd\sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\mu(k)\sum_{k|i}^{\lfloor \frac{n}{d} \rfloor}i\sum_{k|j}^{\lfloor \frac{m}{d} \rfloor}j =d=1ndk=1dnμ(k)kidnikjdmj
= ∑ d = 1 n d ∑ k = 1 ⌊ n d ⌋ μ ( k ) k 2 ∑ i = 1 ⌊ n d k ⌋ i ∑ j = 1 ⌊ m d k ⌋ j = \sum_{d=1}^nd\sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\mu(k)k^2\sum_{i=1}^{\lfloor \frac{n}{dk} \rfloor}i\sum_{j=1}^{\lfloor \frac{m}{dk} \rfloor}j =d=1ndk=1dnμ(k)k2i=1dknij=1dkmj
= ∑ d = 1 n d ∑ k = 1 ⌊ n d ⌋ μ ( k ) k 2 S ( ⌊ n d k ⌋ ) S ( ⌊ m d k ⌋ ) = \sum_{d=1}^nd\sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\mu(k)k^2S(\lfloor \frac{n}{dk} \rfloor)S(\lfloor \frac{m}{dk} \rfloor) =d=1ndk=1dnμ(k)k2S(dkn)S(dkm)
= ∑ k = 1 n μ ( k ) k 2 ∑ d = 1 ⌊ n k ⌋ S ( ⌊ n d k ⌋ ) S ( ⌊ m d k ⌋ ) d = \sum_{k=1}^n\mu(k)k^2\sum_{d=1}^{\lfloor \frac{n}{k} \rfloor}S(\lfloor \frac{n}{dk} \rfloor)S(\lfloor \frac{m}{dk} \rfloor)d =k=1nμ(k)k2d=1knS(dkn)S(dkm)d
= ∑ k = 1 n μ ( k ) k 2 ∑ k ∣ T n S ( ⌊ n T ⌋ ) S ( ⌊ m T ⌋ ) T k = \sum_{k=1}^n\mu(k)k^2\sum_{k|T}^nS(\lfloor \frac{n}{T} \rfloor)S(\lfloor \frac{m}{T} \rfloor)\frac{T}{k} =k=1nμ(k)k2kTnS(Tn)S(Tm)kT
= ∑ T = 1 n S ( ⌊ n T ⌋ ) S ( ⌊ m T ⌋ ) ∑ k ∣ T T T k μ ( k ) k 2 = \sum_{T=1}^nS(\lfloor \frac{n}{T} \rfloor)S(\lfloor \frac{m}{T} \rfloor)\sum_{k|T}^T\frac{T}{k}\mu(k)k^2 =T=1nS(Tn)S(Tm)kTTkTμ(k)k2
= ∑ T = 1 n S ( ⌊ n T ⌋ ) S ( ⌊ m T ⌋ ) T ∑ k ∣ T T μ ( k ) k = \sum_{T=1}^nS(\lfloor \frac{n}{T} \rfloor)S(\lfloor \frac{m}{T} \rfloor)T\sum_{k|T}^T\mu(k)k =T=1nS(Tn)S(Tm)TkTTμ(k)k
f ( n ) = n ∑ d ∣ n μ ( d ) d f(n)=n\sum_{d|n}\mu(d)d f(n)=ndnμ(d)d
a n s = ∑ T = 1 n    S ( ⌊ n T ⌋ ) S ( ⌊ m T ⌋ ) f ( T ) ans= \sum_{T=1}^n\;S(\lfloor \frac{n}{T} \rfloor)S(\lfloor \frac{m}{T} \rfloor) f(T) ans=T=1nS(Tn)S(Tm)f(T)
根据整除分块的相关知识
⌊ n T ⌋ \lfloor \frac{n}{T} \rfloor Tn的变化量只有 O ( n ) O(\sqrt n) O(n )种,所以 S ( ⌊ n T ⌋ ) S(\lfloor \frac{n}{T} \rfloor) S(Tn)的变化量也有 O ( n ) O(\sqrt n) O(n )种,处理剩余部分的前缀和,时间复杂度 O ( n + T n ) O(n+T\sqrt n) O(n+Tn ),其中 T T T为询问次数

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e8+9;
const LL N = 1e7+7;
LL f[N];
LL v[N],prime[N],tot=0;
LL mu[N];
void init(LL n)
{
	f[1]=1;
	mu[1]=1;
	for(LL i=2;i<=n;i++)
	{
		if(!v[i])
		{
			v[i]=i;
			prime[++tot]=i;
			mu[i]=-1;
			f[i]=1+(-1)*i;
			f[i]=(f[i]+mod)%mod;
		}
		for(LL j=1;j<=tot;j++)
		{
			if(prime[j]>v[i]||i*prime[j]>n) break;
			v[prime[j]*i]=prime[j];
			if(i%prime[j]==0)
			{
				f[prime[j]*i]=f[i]; //因为质因子的指数加一,所以新增的因子的莫比乌斯函数之都为0,所以相当于没有加 
				break;
			}
			else f[i*prime[j]]=(f[i]+(-1)*f[i]*prime[j]%mod+mod)%mod;
 //新增一个质因子,这个质因子可以和之前的所有因子组合,也就是f[i]*prime[j],但是长度加一,莫比乌斯函数值变成相反数,所以要乘-1
		}
	}	
	for(LL i=1;i<=n;i++) 
	f[i]=(f[i]*i%mod+f[i-1])%mod;
} 
LL S(LL n)
{
	return 1ll*(1+n)*n/2%mod;
}
int main()
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	init(1e7);
	LL T;
	cin>>T;
	while(T--)
	{
		LL n,m;
		scanf("%lld %lld",&n,&m);
		if(n>m) swap(n,m);
		LL l=1,r;
		LL ans=0;
		for(;l<=n;l=r+1)
		{
			r=min((n/(n/l)),m/(m/l));
			ans=(ans+S(n/l)*S(m/l)%mod*(f[r]-f[l-1]+mod)%mod)%mod;
		}
		printf("%lld\n",ans);
	}
	return 0;
}
[NOI2010day1]T1-能量采集

化简题意可知要求的是
∑ i = 1 n ∑ j = 1 m 2 × g c d ( i , j ) − 1 \sum_{i=1}^n\sum_{j=1}^m2\times gcd(i,j)-1 i=1nj=1m2×gcd(i,j)1
也就是 2 × ∑ i = 1 n ∑ j = 1 m g c d ( i , j ) − n m 2\times \sum_{i=1}^n\sum_{j=1}^m gcd(i,j)-nm 2×i=1nj=1mgcd(i,j)nm
而我们知道 n = ∑ d ∣ n φ ( d ) n=\sum_{d|n}\varphi (d) n=dnφ(d)
g c d ( i , j ) gcd(i,j) gcd(i,j)带入 n n n可得
∑ i = 1 n ∑ j = 1 m ∑ d ∣ i , d ∣ j φ ( d ) \sum_{i=1}^{n}\sum_{j=1}^m\sum_{d|i,d|j}\varphi(d) i=1nj=1mdi,djφ(d)
= ∑ d = 1 n φ ( d ) ∑ d ∣ i n ∑ d ∣ j m 1 =\sum_{d=1}^n\varphi(d)\sum_{d|i}^{n}\sum_{d|j}^m1 =d=1nφ(d)dindjm1
= ∑ d = 1 n φ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ 1 =\sum_{d=1}^n\varphi(d)\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}1 =d=1nφ(d)i=1dnj=1dm1
= ∑ d = 1 n φ ( d ) ⌊ n d ⌋ ⌊ m d ⌋ =\sum_{d=1}^n\varphi(d)\lfloor \frac{n}{d} \rfloor\lfloor \frac{m}{d} \rfloor =d=1nφ(d)dndm
预处理欧拉函数前缀和,再套上整除分块即可

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL N = 1e5+7;
LL phi[N];
LL v[N],prime[N],tot=0;
void init(LL n)
{
	phi[1]=1;
	for(LL i=2;i<=n;i++)
	{
		if(!v[i])
		{
			v[i]=i;
			prime[++tot]=i;
			phi[i]=i-1; 
		}
		for(LL j=1;j<=tot;j++)
		{
			if(v[i]<prime[j]||i*prime[j]>n) break;
			v[i*prime[j]]=prime[j];
			if(v[i]%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}	
	for(LL i=1;i<=n;i++)
	phi[i]=phi[i]+phi[i-1];
}
int main()
{
	freopen("energy.in","r",stdin);
	freopen("energy.out","w",stdout);
	init(1e5);
	LL n,m;
	cin>>n>>m;
	if(n>m) swap(n,m);
	LL ans=0;
	LL l=1,r;
	for(;l<=n;l=r+1)
	{
		r=min((n/(n/l)),(m/(m/l)));
		ans=(ans+1ll*(phi[r]-phi[l-1])*1ll*(n/l)*(m/l));
	}
	cout<<2*ans-n*m;
	return 0;
}
YY的GCD

∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) ∈ p r i m e ] \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)\in prime] i=1nj=1m[gcd(i,j)prime]
化简: ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) ∈ p r i m e ] \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)\in prime] i=1nj=1m[gcd(i,j)prime]
= ∑ i = 1 n ∑ j = 1 m ∑ d ∣ i , d ∣ j [ g c d ( i , j ) = = d ] , d ∈ p r i m e =\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}[gcd(i,j)==d],d\in prime =i=1nj=1mdi,dj[gcd(i,j)==d],dprime
= ∑ d = 1 n ∑ d ∣ i n ∑ d ∣ j m [ g c d ( i , j ) = = d ] , d ∈ p r i m e =\sum_{d=1}^n\sum_{d|i}^n\sum_{d|j}^m[gcd(i,j)==d],d\in prime =d=1ndindjm[gcd(i,j)==d],dprime
= ∑ d = 1 n ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = = 1 ] , d ∈ p r i m e =\sum_{d=1}^n\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}[gcd(i,j)==1],d\in prime =d=1ni=1dnj=1dm[gcd(i,j)==1],dprime
= ∑ d = 1 n ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ k ∣ i , k ∣ j μ ( k ) , d ∈ p r i m e =\sum_{d=1}^n\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}\sum_{k|i,k|j}\mu(k),d\in prime =d=1ni=1dnj=1dmki,kjμ(k),dprime
= ∑ d = 1 n ∑ k = 1 n μ ( k ) ∑ i = 1 ⌊ n d k ⌋ ∑ j = 1 ⌊ m d k ⌋ 1 , d ∈ p r i m e =\sum_{d=1}^n\sum_{k=1}^n\mu(k)\sum_{i=1}^{\lfloor \frac{n}{dk} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{dk} \rfloor}1,d\in prime =d=1nk=1nμ(k)i=1dknj=1dkm1,dprime
= ∑ d = 1 n ∑ k = 1 n μ ( k ) ⌊ n d k ⌋ ⌊ m d k ⌋ , d ∈ p r i m e =\sum_{d=1}^n\sum_{k=1}^n\mu(k)\lfloor \frac{n}{dk} \rfloor\lfloor \frac{m}{dk} \rfloor,d\in prime =d=1nk=1nμ(k)dkndkm,dprime
= ∑ T = 1 n ⌊ n T ⌋ ⌊ m T ⌋ ∑ d ∣ T μ ( T d ) , d ∈ p r i m e =\sum_{T=1}^n\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T} \rfloor\sum_{d|T}\mu(\frac{T}{d}),d\in prime =T=1nTnTmdTμ(dT),dprime
f ( n ) = ∑ d ∣ n , d ∈ p r i m e μ ( T d ) f(n)=\sum_{d|n,d\in prime }\mu(\frac{T}{d}) f(n)=dn,dprimeμ(dT)
a n s = ∑ T = 1 n ⌊ n T ⌋ ⌊ m T ⌋ f ( T ) ans=\sum_{T=1}^n\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T} \rfloor f(T) ans=T=1nTnTmf(T)
预处理 f ( n ) f(n) f(n)的前缀和,这个可以枚举质数 d d d d d d的倍数 n n n算出,因为只枚举质数,所以复杂度 O ( n    l o g n    l o g n ) O(n\;logn\;logn) O(nlognlogn)
接着整除分块即可

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7+7;
int u[N];
int prime[N],top=0;
LL T[N];
LL sum[N];
bool vis[N];
void sieve(int n)
{
	u[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			u[i]=-1;
			prime[++top]=i;
		}
		for(int j=1;j<=top&&i*prime[j]<=n;j++)
		{
			vis[i*prime[j]]=1;
			if(i%prime[j]==0) 
			{
				u[i*prime[j]]=0;
				break;
			}
			else u[i*prime[j]]=u[i]*u[prime[j]];
		}
	}
	for(int j=1;j<=top;j++)
	{
		for(int i=1;i*prime[j]<=n;i++)
		{
			T[i*prime[j]]+=u[i];
		}
	}
	for(int i=1;i<=n;i++)
	sum[i]=sum[i-1]+T[i];
}
LL count(int n,int m)
{
	LL res=0;
	for(int l=1,r=0;l<=n;l=r+1)
	{
		r=min(n/(n/l),m/(m/l));
		res=res+(sum[r]-sum[l-1])*(n/l)*(m/l);
	}
	return res;
}
int main()
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	int T;
	cin>>T;
	sieve(1e7);
	while(T--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		if(n>m) swap(n,m);
		printf("%lld\n",count(n,m));
	}
	return 0;
}
[SDOI2015] 约数个数和

∑ i = 1 n ∑ j = 1 m d ( i j ) \sum_{i=1}^n\sum_{j=1}^md(ij) i=1nj=1md(ij)
其中 d ( n ) d(n) d(n)表示 n n n的约数个数和
首先有引理 d ( i j ) = ∑ x ∣ i ∑ y ∣ j [ g c d ( x , y ) = = 1 ] d(ij)=\sum_{x|i}\sum_{y|j}[gcd(x,y)==1] d(ij)=xiyj[gcd(x,y)==1]
证明详见这篇blog
根据引理,所求即为 ∑ i = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j [ g c d ( x , y ) = = 1 ] \sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[gcd(x,y)==1] i=1nj=1mxiyj[gcd(x,y)==1]
= ∑ x = 1 n ∑ y = 1 m [ g c d ( x , y ) = = 1 ] ∑ x ∣ i ∑ y ∣ j 1 =\sum_{x=1}^n\sum_{y=1}^m[gcd(x,y)==1]\sum_{x|i}\sum_{y|j}1 =x=1ny=1m[gcd(x,y)==1]xiyj1
= ∑ x = 1 n ∑ y = 1 m [ g c d ( x , y ) = = 1 ] ∑ i = 1 ⌊ n x ⌋ ∑ j = 1 ⌊ m y ⌋ 1 =\sum_{x=1}^n\sum_{y=1}^m[gcd(x,y)==1]\sum_{i=1}^{\lfloor \frac{n}{x} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{y} \rfloor}1 =x=1ny=1m[gcd(x,y)==1]i=1xnj=1ym1
= ∑ x = 1 n ∑ y = 1 m [ g c d ( x , y ) = = 1 ] ⌊ n x ⌋ ⌊ m y ⌋ =\sum_{x=1}^n\sum_{y=1}^m[gcd(x,y)==1]\lfloor \frac{n}{x} \rfloor\lfloor \frac{m}{y} \rfloor =x=1ny=1m[gcd(x,y)==1]xnym
= ∑ x = 1 n ∑ y = 1 m ∑ d ∣ x , d ∣ y μ ( d ) ⌊ n x ⌋ ⌊ m y ⌋ =\sum_{x=1}^n\sum_{y=1}^m\sum_{d|x,d|y}\mu(d)\lfloor \frac{n}{x} \rfloor\lfloor \frac{m}{y} \rfloor =x=1ny=1mdx,dyμ(d)xnym
= ∑ d = 1 n μ ( d ) ∑ d ∣ x n ∑ d ∣ y m ⌊ n x ⌋ ⌊ m y ⌋ =\sum_{d=1}^n\mu(d)\sum_{d|x}^n\sum_{d|y}^m\lfloor \frac{n}{x} \rfloor\lfloor \frac{m}{y} \rfloor =d=1nμ(d)dxndymxnym
= ∑ d = 1 n μ ( d ) ∑ x = 1 ⌊ n d ⌋ ∑ y = 1 ⌊ m d ⌋ ⌊ n d x ⌋ ⌊ m d y ⌋ =\sum_{d=1}^n\mu(d)\sum_{x=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{y=1}^{\lfloor \frac{m}{d} \rfloor}\lfloor \frac{n}{dx} \rfloor\lfloor \frac{m}{dy} \rfloor =d=1nμ(d)x=1dny=1dmdxndym
= ∑ d = 1 n μ ( d ) ∑ x = 1 ⌊ n d ⌋ ⌊ n d x ⌋ ∑ y = 1 ⌊ m d ⌋ ⌊ m d y ⌋ =\sum_{d=1}^n\mu(d)\sum_{x=1}^{\lfloor \frac{n}{d} \rfloor}\lfloor \frac{n}{dx} \rfloor \sum_{y=1}^{\lfloor \frac{m}{d} \rfloor}\lfloor \frac{m}{dy} \rfloor =d=1nμ(d)x=1dndxny=1dmdym
f ( n ) = ∑ i = 1 n ⌊ n i ⌋ f(n)=\sum_{i=1}^n\lfloor \frac{n}{i}\rfloor f(n)=i=1nin, N = ⌊ n d ⌋ , M = ⌊ m d ⌋ N=\lfloor \frac{n}{d} \rfloor,M=\lfloor \frac{m}{d} \rfloor N=dn,M=dm
= ∑ d = 1 n μ ( d ) ∑ x = 1 N ⌊ N x ⌋ ∑ y = 1 M ⌊ M y ⌋ =\sum_{d=1}^n\mu(d)\sum_{x=1}^{N}\lfloor \frac{N}{x} \rfloor \sum_{y=1}^{M}\lfloor \frac{M}{y} \rfloor =d=1nμ(d)x=1NxNy=1MyM
= ∑ d = 1 n μ ( d ) f ( N ) f ( M ) =\sum_{d=1}^n\mu(d)f(N)f(M) =d=1nμ(d)f(N)f(M)
= ∑ d = 1 n μ ( d ) f ( ⌊ n d ) f ( ⌊ m d ⌋ ) =\sum_{d=1}^n\mu(d)f(\lfloor \frac{n}{d})f(\lfloor \frac{m}{d} \rfloor) =d=1nμ(d)f(dn)f(dm)
这个形式就可以很舒服的整除分块了
f ( n ) f(n) f(n)可以整除分块 O ( n n ) O(n\sqrt n) O(nn )预处理
总复杂度 O ( n n + T n ) O(n\sqrt n+T\sqrt n) O(nn +Tn ), T T T是询问次数

#include<bits/stdc++.h>
using namespace std; 
typedef long long LL;
const int N = 5e4+100;
inline int read()
{
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
LL prime[N],tot,u[N],sum[N],f[N];
bool vis[N];
void prework(int n)
{
	u[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			prime[++tot]=i;
			u[i]=-1;
		}
		for(int j=1;j<=tot&&prime[j]*i<=n;j++)
		{
			int p=prime[j];
			vis[i*p]=1;
			if(i%p==0) break;
			else u[i*p]=u[i]*u[p];
		}
	}
	for(int i=1;i<=n;i++)
	sum[i]=sum[i-1]+u[i];
	for(int i=1;i<=n;i++)
	{
		for(int l=1,r;l<=i;l=r+1)
		{
			r=(i/(i/l));
			f[i]=f[i]+1ll*(r-l+1)*1ll*(i/l);
		}
	}
}
int main()
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	int t,n,m;
	cin>>t;
	prework(50000);
	while(t--)
	{
		n=read();
		m=read();
		LL range=min(n,m);
		LL ans=0;
		for(int l=1,r;l<=range;l=r+1)
		{
			r=min(n/(n/l),m/(m/l));
			ans=ans+(sum[r]-sum[l-1])*1ll*f[n/l]*1ll*f[m/l];
		}
		printf("%lld\n",ans);
	}
	return 0;
}
[HAOI2011]Problem b

∑ l ≤ i ≤ r ∑ a ≤ j ≤ b [ g c d ( i , j ) = = k ] \sum_{l\leq i \leq r}\sum_{a\leq j\leq b}[gcd(i,j)==k] lirajb[gcd(i,j)==k]
观察到这类似于二维前缀和,我们只需要求出这样一个式子就行了
∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = = k ] \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==k] i=1nj=1m[gcd(i,j)==k]
N = ⌊ n k ⌋ N=\lfloor \frac{n}{k} \rfloor N=kn, M = ⌊ m k ⌋ M=\lfloor \frac{m}{k} \rfloor M=km
= ∑ i = 1 N ∑ j = 1 M [ g c d ( i , j ) = = 1 ] =\sum_{i=1}^{N}\sum_{j=1}^{M}[gcd(i,j)==1] =i=1Nj=1M[gcd(i,j)==1]
= ∑ i = 1 N ∑ j = 1 M ∑ d ∣ i , d ∣ j μ ( d ) =\sum_{i=1}^{N}\sum_{j=1}^{M}\sum_{d|i,d|j}\mu(d) =i=1Nj=1Mdi,djμ(d)
= ∑ d = 1 N μ ( d ) ∑ d ∣ i N ∑ d ∣ j M 1 =\sum_{d=1}^N\mu(d) \sum_{d|i}^{N}\sum_{d|j}^{M}1 =d=1Nμ(d)diNdjM1
= ∑ d = 1 N μ ( d ) ∑ i = 1 ⌊ N d ⌋ ∑ j = 1 ⌊ M d ⌋ 1 =\sum_{d=1}^N\mu(d) \sum_{i=1}^{\lfloor \frac{N}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{M}{d} \rfloor}1 =d=1Nμ(d)i=1dNj=1dM1
= ∑ d = 1 N μ ( d ) ⌊ N d ⌋ ⌊ M d ⌋ =\sum_{d=1}^N\mu(d)\lfloor \frac{N}{d} \rfloor\lfloor \frac{M}{d} \rfloor =d=1Nμ(d)dNdM
直接整除分块即可

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
int u[N],vis[N];
int prime[N],top=0;
int sum[N];
void sieve(int n)
{
	u[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(vis[i]==0)
		{
			prime[++top]=i;
			u[i]=-1;
		}
		for(int j=1;j<=top&&prime[j]*i<=n;j++)
		{
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				u[i*prime[j]]=0;
				break;
			}
			else u[i*prime[j]]=u[i]*u[prime[j]];
		}
	}
	for(int i=1;i<=n;i++)
	sum[i]=sum[i-1]+u[i];
}
int count(int a,int b,int d)
{		
	a/=d;
	b/=d;
	int res=0;
	if(a>b) swap(a,b);
	for(int L=1,R=0;L<=a;L=R+1)
	{
		R=min(a/(a/L),b/(b/L));
		res=res+(sum[R]-sum[L-1])*(a/L)*(b/L);
	}	
	return res;
}
int main()
{
	sieve(1e5+7);
	int T;
	cin>>T;
	while(T--)
	{
		int a,b,c,d,k;
		scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
		printf("%d\n",count(b,d,k)-count(b,c-1,k)-count(a-1,d,k)+count(a-1,c-1,k));
	 } 
	return 0;
} 
BZOJ4407 于神之怒加强版

∑ i = 1 n ∑ j = 1 m gcd ⁡ ( i , j ) k     m o d   1 0 9 + 7 \sum_{i=1}^n\sum_{j=1}^m\gcd(i,j)^k\ \bmod 10^9+7 i=1nj=1mgcd(i,j)k mod109+7
化简
∑ i = 1 n ∑ j = 1 m gcd ⁡ ( i , j ) k \sum_{i=1}^n\sum_{j=1}^m\gcd(i,j)^k i=1nj=1mgcd(i,j)k
= ∑ i = 1 n ∑ j = 1 m ∑ d ∣ i , d ∣ j [ g c d ( i , j ) = = d ] d k =\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}[gcd(i,j)==d]d^k =i=1nj=1mdi,dj[gcd(i,j)==d]dk
= ∑ d = 1 n d k ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = = d ] =\sum_{d=1}^n d^k \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==d] =d=1ndki=1nj=1m[gcd(i,j)==d]
= ∑ d = 1 n d k ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = = 1 ] =\sum_{d=1}^n d^k \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}[gcd(i,j)==1] =d=1ndki=1dnj=1dm[gcd(i,j)==1]
= ∑ d = 1 n d k ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ k ∣ i , k ∣ j μ ( k ) =\sum_{d=1}^n d^k \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}\sum_{k|i,k|j}\mu(k) =d=1ndki=1dnj=1dmki,kjμ(k)
= ∑ d = 1 n d k ∑ k = 1 ⌊ n d ⌋ μ ( k ) ∑ k ∣ i ⌊ n d ⌋ ∑ k ∣ j ⌊ m d ⌋ 1 =\sum_{d=1}^n d^k\sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\mu(k) \sum_{k|i}^{\lfloor \frac{n}{d} \rfloor}\sum_{k|j}^{\lfloor \frac{m}{d} \rfloor}1 =d=1ndkk=1dnμ(k)kidnkjdm1
= ∑ d = 1 n d k ∑ k = 1 ⌊ n d ⌋ μ ( k ) ∑ i = 1 ⌊ n d k ⌋ ∑ j = 1 ⌊ m d k ⌋ 1 =\sum_{d=1}^n d^k\sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\mu(k) \sum_{i=1}^{\lfloor \frac{n}{dk} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{dk} \rfloor}1 =d=1ndkk=1dnμ(k)i=1dknj=1dkm1
= ∑ d = 1 n d k ∑ k = 1 ⌊ n d ⌋ μ ( k ) ⌊ n d k ⌋ ⌊ m d k ⌋ =\sum_{d=1}^n d^k\sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\mu(k) \lfloor \frac{n}{dk} \rfloor\lfloor \frac{m}{dk} \rfloor =d=1ndkk=1dnμ(k)dkndkm
= ∑ T = 1 n ⌊ n T ⌋ ⌊ m T ⌋ ∑ d ∣ T n d k μ ( n d ) =\sum_{T=1}^n\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T} \rfloor\sum_{d|T}^n d^k \mu({ \frac{n}{d} }) =T=1nTnTmdTndkμ(dn)
注意因为枚举了倍数,所以k前的 ∑ \sum 就被提前了,不能算
f ( n ) = ∑ d ∣ n d k μ ( n d ) f(n)=\sum_{d|n}d^k\mu(\frac{n}{d}) f(n)=dndkμ(dn)
那么 f ( n ) f(n) f(n)是两个积性函数的狄利克雷卷积,所以 f ( n ) f(n) f(n)是积性函数
直接线性筛晒一下就行了

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N =5e6+7;
const int mod = 1e9+7;
LL Pow(LL a,LL b)
{
	LL res=1;
	while(b)
	{
		if(b&1) res=1ll*res*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return res;
} 
int v[N],prime[N],tot=0;
LL mu[N],pk[N],f[N];
int k;
void init(int n)
{
	mu[1]=1;
	f[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!v[i])
		{
			v[i]=i;
			prime[++tot]=i;
			pk[i]=Pow(i,k);
			f[i]=(pk[i]-1+mod)%mod;
		}
		for(int j=1;j<=tot;j++)
		{
			if(prime[j]>v[i]||i*prime[j]>n) break;
			v[i*prime[j]]=prime[j];
			if(i%prime[j]==0)
			{
				f[i*prime[j]]=1ll*f[i]*pk[prime[j]]%mod;
				break;
			}
			else f[i*prime[j]]=1ll*f[i]*f[prime[j]];
		}
	}
	for(int i=1;i<=n;i++)
	f[i]=(f[i]+f[i-1])%mod;
 } 
int main()
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	int T;
	cin>>T>>k;
	init(5e6);
	while(T--)
	{
		int n,m;
		scanf("%d %d",&n,&m);
		if(n>m) swap(n,m);
		LL ans=0;
		int l=1,r;
		for(;l<=n;l=r+1)
		{
			r=min(n/(n/l),m/(m/l));
			ans=(ans+(f[r]-f[l-1]+mod)%mod*(n/l)%mod*(m/l)%mod)%mod; 
		}
		printf("%lld\n",ans);
	}
	return 0;
}

技巧总结

1:可以枚举要求的数,比如gcd,然后用布尔表达式写出来
2:将某些项提前,然后将和它相关的项用改成枚举倍数,使上指标方便进行整除分块
3:一般情况最终有两种情况:
一是可以直接写成整除分块的形式
二是整除下有两个字母的乘积,此时可以枚举乘积,并将乘积提前,此时其余的项是封闭的,且一般可以写成一个关于乘积的函数,可以用线性筛预处理出

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值