Loading

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

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);
	}

莫比乌斯函数

\(\mu (n)\)\(n\)的莫比乌斯函数,其中$$n=\Pi_{p_i}^{c_i}$$
则若\(c_i\)不等于1,\(\mu(n)=0\)
\(p_i\)的个数为\(k\),则\(\mu(n)=(-1)^k\)
特殊的,\(\mu(1)=1\)
性质:\(\sum_{d|n}\mu(d)=[n==1]\)
\(\mu(n)=-\sum_{d|n ,d!=n}\mu(d))\)
\(\mu(n)=\mu(a)\mu(b)\),其中\(n=a\times b\),且\(gcd(a,b)=1\)

狄雷克雷卷积

\[h(n)=\sum _{d|n}f(n)g(n/d) \]

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

莫比乌斯反演

反演公式:记\(f(n)=\sum_{d|n}g(n)\)
\(g(n)=\sum_{d|n}f(d)\mu(n/d)\)
原理:$$f = g\times I$$

\[f\times \mu=g \times e \]

\[g=f\times \mu \]

例题

1:周期性的字符串

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

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

分析

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

\(\sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]\)
因为\(\sum_{d|n}\mu(d)=[n==1]\)
\(n\)换成\(gcd(i,j)\)可知,所求为\(\sum_{i=1}^n\sum_{j=1}^n\sum_{d|i,d|j}\mu(d)\)
\(d\)提前,\(\sum_{d=1}^n\mu(d)\sum_{d|i}\sum_{d|j}1\)
\(i,j\)换成倍数可得

\[\sum_{d=1}^n\mu(d)\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{d} \rfloor} \]

\[=\sum_{d=1}^n\mu(d)\lfloor \frac{n}{d} \rfloor \lfloor \frac{n}{d} \rfloor \]

#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

\[\sum_{i=1}^n\sum_{j=1}^mlcm(i,j) \]

思路

\(S(n)=\sum_{i=1}^ni\)

\[\sum_{i=1}^n\sum_{j=1}^mlcm(i,j)=\sum_{i=1}^n\sum_{j=1}^m\frac{ij}{gcd(i,j)} \]

\[=\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}[gcd(i,j)==d]\frac{ij}{d} \]

\[= \sum_{d=1}^n\sum_{d|i}\sum_{d|j}[gcd(i,j)==d]\frac{ij}{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} \]

\[= \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) \]

\[= \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 \]

\[= \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 \]

\[= \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) \]

\[= \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 \]

\[= \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} \]

\[= \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 \]

\[= \sum_{T=1}^nS(\lfloor \frac{n}{T} \rfloor)S(\lfloor \frac{m}{T} \rfloor)T\sum_{k|T}^T\mu(k)k \]

\(f(n)=n\sum_{d|n}\mu(d)d\)

\[ans= \sum_{T=1}^n\;S(\lfloor \frac{n}{T} \rfloor)S(\lfloor \frac{m}{T} \rfloor) f(T) \]

根据整除分块的相关知识
\(\lfloor \frac{n}{T} \rfloor\)的变化量只有\(O(\sqrt n)\)种,所以\(S(\lfloor \frac{n}{T} \rfloor)\)的变化量也有\(O(\sqrt n)\)种,处理剩余部分的前缀和,时间复杂度\(O(n+T\sqrt n)\),其中\(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-能量采集

化简题意可知要求的是

\[\sum_{i=1}^n\sum_{j=1}^m2\times gcd(i,j)-1 \]

也就是

\[2\times \sum_{i=1}^n\sum_{j=1}^m gcd(i,j)-nm \]

而我们知道\(n=\sum_{d|n}\varphi (d)\)
\(gcd(i,j)\)带入\(n\)可得

\[\sum_{i=1}^{n}\sum_{j=1}^m\sum_{d|i,d|j}\varphi(d) \]

\[=\sum_{d=1}^n\varphi(d)\sum_{d|i}^{n}\sum_{d|j}^m1 \]

\[=\sum_{d=1}^n\varphi(d)\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}1 \]

\[=\sum_{d=1}^n\varphi(d)\lfloor \frac{n}{d} \rfloor\lfloor \frac{m}{d} \rfloor \]

预处理欧拉函数前缀和,再套上整除分块即可

#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

\[\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)\in prime] \]

化简:

\[\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)\in prime] \]

\[=\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}[gcd(i,j)==d],d\in prime \]

\[=\sum_{d=1}^n\sum_{d|i}^n\sum_{d|j}^m[gcd(i,j)==d],d\in prime \]

\[=\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 \]

\[=\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 \]

\[=\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 \]

\[=\sum_{d=1}^n\sum_{k=1}^n\mu(k)\lfloor \frac{n}{dk} \rfloor\lfloor \frac{m}{dk} \rfloor,d\in prime \]

\[=\sum_{T=1}^n\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T} \rfloor\sum_{d|T}\mu(\frac{T}{d}),d\in prime \]

\(f(n)=\sum_{d|n,d\in prime }\mu(\frac{T}{d})\)

\[ans=\sum_{T=1}^n\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T} \rfloor f(T) \]

预处理\(f(n)\)的前缀和,这个可以枚举质数\(d\)\(d\)的倍数\(n\)算出,因为只枚举质数,所以复杂度\(O(n\;logn\;logn)\)
接着整除分块即可

#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] 约数个数和

\[\sum_{i=1}^n\sum_{j=1}^md(ij) \]

其中\(d(n)\)表示\(n\)的约数个数和
首先有引理

\[d(ij)=\sum_{x|i}\sum_{y|j}[gcd(x,y)==1] \]

证明详见这篇blog
根据引理,所求即为

\[\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[gcd(x,y)==1] \]

\[=\sum_{x=1}^n\sum_{y=1}^m[gcd(x,y)==1]\sum_{x|i}\sum_{y|j}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 \]

\[=\sum_{x=1}^n\sum_{y=1}^m[gcd(x,y)==1]\lfloor \frac{n}{x} \rfloor\lfloor \frac{m}{y} \rfloor \]

\[=\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 \]

\[=\sum_{d=1}^n\mu(d)\sum_{d|x}^n\sum_{d|y}^m\lfloor \frac{n}{x} \rfloor\lfloor \frac{m}{y} \rfloor \]

\[=\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 \]

\[=\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 \]

\(f(n)=\sum_{i=1}^n\lfloor \frac{n}{i}\rfloor\),\(N=\lfloor \frac{n}{d} \rfloor,M=\lfloor \frac{m}{d} \rfloor\)

\[=\sum_{d=1}^n\mu(d)\sum_{x=1}^{N}\lfloor \frac{N}{x} \rfloor \sum_{y=1}^{M}\lfloor \frac{M}{y} \rfloor \]

\[=\sum_{d=1}^n\mu(d)f(N)f(M) \]

\[=\sum_{d=1}^n\mu(d)f(\lfloor \frac{n}{d})f(\lfloor \frac{m}{d} \rfloor) \]

这个形式就可以很舒服的整除分块了
\(f(n)\)可以整除分块\(O(n\sqrt n)\)预处理
总复杂度\(O(n\sqrt n+T\sqrt n)\),\(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

\(\sum_{l\leq i \leq r}\sum_{a\leq j\leq b}[gcd(i,j)==k]\)
观察到这类似于二维前缀和,我们只需要求出这样一个式子就行了

\[\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==k] \]

\(N=\lfloor \frac{n}{k} \rfloor\),\(M=\lfloor \frac{m}{k} \rfloor\)

\[=\sum_{i=1}^{N}\sum_{j=1}^{M}[gcd(i,j)==1] \]

\[=\sum_{i=1}^{N}\sum_{j=1}^{M}\sum_{d|i,d|j}\mu(d) \]

\[=\sum_{d=1}^N\mu(d) \sum_{d|i}^{N}\sum_{d|j}^{M}1 \]

\[=\sum_{d=1}^N\mu(d) \sum_{i=1}^{\lfloor \frac{N}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{M}{d} \rfloor}1 \]

\[=\sum_{d=1}^N\mu(d)\lfloor \frac{N}{d} \rfloor\lfloor \frac{M}{d} \rfloor \]

直接整除分块即可

#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 于神之怒加强版

\[\sum_{i=1}^n\sum_{j=1}^m\gcd(i,j)^k\ \bmod 10^9+7 \]

化简

\[\sum_{i=1}^n\sum_{j=1}^m\gcd(i,j)^k \]

\[=\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}[gcd(i,j)==d]d^k \]

\[=\sum_{d=1}^n d^k \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==d] \]

\[=\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] \]

\[=\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) \]

\[=\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 \]

\[=\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 \]

\[=\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 \]

\[=\sum_{T=1}^n\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T} \rfloor\sum_{d|T}^n d^k \mu({ \frac{n}{d} }) \]

注意因为枚举了倍数,所以k前的\(\sum\)就被提前了,不能算
设$$f(n)=\sum_{d|n}d^k\mu(\frac{n}{d})$$
那么\(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:一般情况最终有两种情况:
一是可以直接写成整除分块的形式
二是整除下有两个字母的乘积,此时可以枚举乘积,并将乘积提前,此时其余的项是封闭的,且一般可以写成一个关于乘积的函数,可以用线性筛预处理出

posted @ 2022-01-24 16:15  Larunatrecy  阅读(66)  评论(0)    收藏  举报