前置知识:积性函数。具体可以看我的博客(积性函数专题) https://www.cnblogs.com/czyty114/p/12707134.html
莫比乌斯反演
(;)
如果(F(n)=sum_{d|n} f(d))
则 (f(n)=sum_{d|n} mu (d) F(frac{n}{d}))
证明
(;)
看到网上很多人的证明都是推了一大堆的式子,本人表示瑟瑟发抖,这里推荐神葱的一种简单证明方法。
显然,(f*epsilon=f)
(Q.E.D)
(;)
(;)
几道经典的好题
Problem 1
(;)
求:(sum_{i=1}^n sum_{j=1}^m gcd(i,j);;;;n,m leq 10^7)
在含有多个(sum)和(prod)这样的式子中,你要知道有几个基本套路(增加枚举量,交换枚举量),下面会一一说明
啥都别说了,推式子吧。
增加枚举量:
交换枚举量:
所以我们直接预处理出(phi (1-min(n,m)))即可
时间复杂度:(O(n))
题目地址: https://www.luogu.com.cn/problem/U114004
(;)
(;)
Problem 2
(;)
求:(sum_{i=1}^n sum_{j=1}^m lcm(i,j);;;;n,m leq 10^7)
感觉和上道题差不多?其实要难些。
推导:
根据(lcm(i,j)=frac{ij}{gcd(i,j)})
枚举(gcd(i,j)=d)
把(d)提出来
我们把后面的式子挑出来。
根据(epsilon=mu * I)
枚举(d)(交换枚举量)
我们再把后面这个玩意提出来
所以(h(n,m))可以(O(1))求解
由此我们可以得到
而
这样的复杂度是多少呢?
首先我们枚举(d),然后计算(S(lfloor frac{n}{d}
floor,lfloor frac{m}{d}
floor))
由于算(S(n,m))是(O(min(n,m)))的
所以最终的复杂度大约是(O(frac{n}{1}+frac{n}{2}+cdots +frac{n}{n})=O(n;log;n))
但是(10^7)貌似不太行啊。
所以就要用到整除分块,用(O(sqrt n))分别计算:
和
即可
时间复杂度:(O(n))
题目地址: https://www.luogu.com.cn/problem/P1829
(Code:)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=10000010,mod=20101009;
int n,m,phi[N],mu[N],prime[N],cnt,res,st[N],sum[N];
void qwq(int n)
{
phi[1]=1;mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!st[i])
{
prime[++cnt]=i;
phi[i]=i-1;
mu[i]=-1;
}
for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
{
st[i*prime[j]]=1;
if(i%prime[j])
{
phi[i*prime[j]]=phi[i]*phi[prime[j]];
mu[i*prime[j]]=mu[i]*mu[prime[j]];
}
else
{
phi[i*prime[j]]=phi[i]*prime[j];
mu[i*prime[j]]=0;
break;
}
}
}
}
int h(int n,int m)
{
return (1LL*n*(n+1)/2%mod)*(1LL*m*(m+1)/2%mod)%mod;
}
int S(int n,int m)
{
int ans=0;
for(int l=1,r;l<=min(n,m);l=r+1)
{
r=min(n/(n/l),m/(m/l));
ans=(ans+1LL*(sum[r]+mod-sum[l-1])%mod*h(n/l,m/l)%mod)%mod;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
qwq(min(n,m));
for(int d=1;d<=min(n,m);d++)
{
sum[d]=(sum[d-1]+1LL*(mu[d]+mod)*d%mod*d%mod)%mod;
}
for(int l=1,r;l<=n;l=r+1)
{
if(n/l==0||m/l==0)break;
r=min(n/(n/l),m/(m/l));
res=(res+1LL*(r-l+1)*(l+r)/2%mod*S(n/l,m/l)%mod)%mod;
}
printf("%d",res);
return 0;
}
(;)
(;)
Problem 3
求:(1leq xleq n,1leq yleq m)且(gcd(x,y))为质数的((x,y))有多少对
(T)组询问。(1leq n,m leq 10^7,Tleq 10^4)
即:
推导:
枚举prime
常规套路,消去一个(prim_k)
而(lfloor frac{lfloor frac{a}{b} floor}{c} floor=lfloor frac{a}{bc} floor)
如果简单的枚举(prime),然后你就开心地发现你(TLE)了。
有一个十分巧妙的方法:
设(T=prim_k d)
然后我们枚举(T),即可得到下面的式子:
然后你发现:(sum_{prim_k|T} mu (frac{T}{prim_k}))这个东西是可以预处理的。
即:枚举每个质数的倍数,把(mu)加到倍数里面
for(int i=1;i<=cnt;i++)
{
for(int j=1;prime[i]*j<=N-10;j++)
{
cur[prime[i]*j]+=mu[j];
}
}
所以我们对于每组询问,用上述方法,再加上整除分块的优化,即可(O(sqrt n))求出答案
时间复杂度:(O(T imes sqrt n))
(Code:)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=10000010;
typedef long long LL;
int n,m,T,prime[N],cnt,mu[N],st[N],sum[N],cur[N];
int sumcur[N];
void qwq(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!st[i])
{
prime[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
{
st[i*prime[j]]=1;
if(i%prime[j])
{
mu[i*prime[j]]=mu[i]*mu[prime[j]];
}
else
{
mu[i*prime[j]]=0;
break;
}
}
}
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+mu[i];
}
LL Query(int n,int m)
{
LL res=0;
for(int S=1,r;S<=min(n,m);S=r+1)
{
if(n/S==0||m/S==0)break;
r=min(n/(n/S),m/(m/S));
res=res+1LL*(n/S)*(m/S)*(sumcur[r]-sumcur[S-1]);
}
return res;
}
int main()
{
scanf("%d",&T);
qwq(N-10);
for(int i=1;i<=cnt;i++)
{
for(int j=1;prime[i]*j<=N-10;j++)
{
cur[prime[i]*j]+=mu[j];
}
}
for(int i=1;i<=N-10;i++)sumcur[i]=sumcur[i-1]+cur[i];
while(T--)
{
scanf("%d%d",&n,&m);
printf("%lld
",Query(n,m));
}
return 0;
}
(;)
题目地址: https://www.luogu.com.cn/problem/P2257