f(n)表示某一范围内(x,y)=n的数对的数量,F(n)表示某一范围内n|(x,y)的数对的数量
那么直接求f(n)并不是很好求,而F(n)求起来相对无脑一些,我们可以通过对F(n)进行莫比乌斯反演来求得f(n)
线性筛μ模板:
void Prime(int N)
{
mu[1]=1;int cnt=0;
for(int i=2;i<=N;i++)
{
if(!v[i]) p[++cnt]=i,mu[i]=-1;
for(int j=1,k;j<=cnt&&p[j]*i<=N;j++)
{
v[k=p[j]*i]=1;
if(i%p[j]==0) {mu[k]=0;break;}
mu[k]=-mu[i];
}
}
}
P S 1 : f o r ( . . . j < = c n t . . . ) 不 要 漏 写 , 会 R E PS1:for(...j<=cnt...)不要漏写,会RE PS1:for(...j<=cnt...)不要漏写,会RE
基本解题思路:枚举gcd
例题1:
求1<=i<=n, 1<=j<=m, gcd(i,j)=k 的(i,j)对数
该问题等价于求1<=i<=n/k (N), 1<=j<=m/k (M) gcd(i,j)=1 的(i,j)对数
f
(
1
)
=
∑
1
∣
d
μ
(
d
1
)
∗
F
(
d
)
=
∑
d
μ
(
d
)
∗
[
N
d
]
[
M
d
]
f(1)=\sum_{1|d}μ(\frac{d}1)*F(d)=\sum_dμ(d)*[\frac{N}d][\frac{M}d]
f(1)=1∣d∑μ(1d)∗F(d)=d∑μ(d)∗[dN][dM]
由
[
N
d
]
[
M
d
]
[\frac{N}d][\frac{M}d]
[dN][dM]的取值是
n
\sqrt{n}
n级的,可以得出:
优化技巧一: 前缀和+分块优化
long long ans=0;
for(int i=1,j;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
ans+=1ll*(n/i)*(m/i)*(sum[j]-sum[i-1]);
}
P
S
2
:
i
=
j
+
1
不
要
写
成
i
+
+
PS2:i=j+1不要写成i++
PS2:i=j+1不要写成i++
P
S
3
:
不
要
忘
乘
1
l
l
!
!
!
PS3:不要忘乘1ll!!!
PS3:不要忘乘1ll!!!
例题2:
求1<=i<=n, 1<=j<=m, gcd(i,j)为质数 的(i,j)对数
优化技巧二: 切换枚举次序
令上式的pd=T, 则有
应用技巧一即可,后面的前缀和可以枚举质数O(n/ln(n) * ln(n)) = O(n)预处理
例题3:
设 d ( x ) d(x) d(x)为 x x x的约数个数,给定N,M,T
求 ∑ i = 1 N ∑ j = 1 M d ( i j ) \sum_{i=1}^N\sum_{j=1}^Md(ij) ∑i=1N∑j=1Md(ij)
N,M,T<=50000
题目解析见这里:约数个数和 (莫比乌斯反演)
注意这里有个神奇的结论:(d(n)表示n的约数个数)
d
(
i
j
)
=
∑
x
∣
i
∑
y
∣
j
[
(
x
,
y
)
=
=
1
]
d(ij)=\sum_{x|i}\sum_{y|j}[(x,y)==1]
d(ij)=x∣i∑y∣j∑[(x,y)==1]
于是算不上技巧但是很神奇的技巧三:
技巧三:替换条件框
[ ( x , y ) = = 1 ] < = > ∑ k ∣ ( x , y ) μ ( k ) [(x,y)==1] <=>\sum_{k|(x,y)}μ(k) [(x,y)==1]<=>∑k∣(x,y)μ(k)
例题3 代码
#include<cstdio>
#include<algorithm>
#define maxn 50005
using namespace std;
int T,n,m,p[maxn],mu[maxn],sum[maxn],mx[maxn];//线性筛约数个数
bool v[maxn];
void Prime(int N)
{
mu[1]=sum[1]=1;int cnt=0;
for(int i=2;i<=N;i++)
{
if(!v[i]){
p[++cnt]=i,mu[i]=-1;
sum[i]=mx[i]=2;
}
for(int j=1,k;j<=cnt&&p[j]*i<=N;j++)
{
v[k=p[j]*i]=1;
if(i%p[j]==0){
mu[k]=0;
mx[k]=mx[i]+1;
sum[k]=sum[i]/mx[i]*mx[k];
break;
}
mu[k]=-mu[i];
mx[k]=2;
sum[k]=sum[i]*2;
}
}
for(int i=1;i<=N;i++) mu[i]+=mu[i-1],sum[i]+=sum[i-1];
}
int main()
{
Prime(maxn-5);
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);if(n>m) swap(n,m);
long long ans=0;
for(int i=1,j;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
ans+=1ll*sum[n/i]*sum[m/i]*(mu[j]-mu[i-1]);
}
printf("%lld\n",ans);
}
}