【题目】
题目描述:
神犇 YY 虐完数论后给傻 × × × kAc 出了一题:
给定 n , m n, m n,m,求 1 ≤ x ≤ n 1≤x≤n 1≤x≤n, 1 ≤ y ≤ m 1≤y≤m 1≤y≤m 且 gcd ( x , y ) \gcd(x, y) gcd(x,y) 为质数的 ( x , y ) (x, y) (x,y) 有多少对。
kAc 这种傻 × × ×必然不会了,于是向你来请教 … … …… ……
输入格式:
第一行一个整数 T T T,表示数据组数。
接下来 T T T 行,每行两个正整数,表示 n , m n, m n,m。
输出格式:
T T T 行,每行一个整数表示第 i i i 组数据的结果
样例数据:
输入
2
10 10
100 100
输出
30
2791
提示:
对于 100 % 100\% 100% 的数据, T ≤ 10000 T ≤ 10000 T≤10000, n , m ≤ 10000000 n, m ≤ 10000000 n,m≤10000000
【分析】
题目要求的是这个东西:
∑ i = 1 n ∑ j = 1 m [    gcd ( i , j )    i s    a    p r i m e    ] \sum_{i=1}^n\sum_{j=1}^m[\;\gcd(i,j)\;\mathrm {is\; a\; prime}\;] i=1∑nj=1∑m[gcd(i,j)isaprime]
如果用 P \mathbb{P} P 表示素数集的话,上式就是
∑ p ∈ P ∑ i = 1 n ∑ j = 1 m [    gcd ( i , j ) = p    ] \sum_{p\in \mathbb{P}}\sum_{i=1}^n\sum_{j=1}^m[\;\gcd(i,j)=p\;] p∈P∑i=1∑nj=1∑m[gcd(i,j)=p]
我们定义
f ( x ) = ∑ i = 1 n ∑ j = 1 m [    gcd ( i , j ) = x    ] f(x)=\sum_{i=1}^n\sum_{j=1}^m[\;\gcd(i,j)=x\;] f(x)=i=1∑nj=1∑m[gcd(i,j)=x]
F ( x ) = ∑ x ∣ d f ( d ) F(x)=\sum_{x|d}f(d) F(x)=x∣d∑f(d)
显然,我们可以得到
F ( x ) = ∑ i = 1 n ∑ j = 1 m [    x ∣ gcd ( i , j )    ] = ⌊ n x ⌋ ⌊ m x ⌋ F(x)=\sum_{i=1}^n\sum_{j=1}^m[\;x|\gcd(i,j)\;]=\lfloor \frac n x\rfloor\lfloor \frac m x\rfloor F(x)=i=1∑nj=1∑m[x∣gcd(i,j)]=⌊xn⌋⌊xm⌋
用莫比乌斯反演得到
f ( x ) = ∑ x ∣ d μ ( d x ) F ( d ) f(x)=\sum_{x|d}\mu(\frac{d}{x})F(d) f(x)=x∣d∑μ(xd)F(d)
那么,最后的答案就是
∑ p ∈ P f ( p ) = ∑ p ∈ P ∑ p ∣ d μ ( d p ) ⌊ n d ⌋ ⌊ m d ⌋ \sum_{p\in\mathbb{P}}f(p)=\sum_{p\in\mathbb P}\sum_{p|d}\mu(\frac{d}{p})\lfloor \frac n d\rfloor\lfloor \frac m d\rfloor p∈P∑f(p)=p∈P∑p∣d∑μ(pd)⌊dn⌋⌊dm⌋
我们把 d d d 改为枚举 p p p 的几倍,即
∑ p ∈ P ∑ d = 1 ⌊ m i n ( n , m ) p ⌋ μ ( d ) ⌊ n d ⋅ p ⌋ ⌊ m d ⋅ p ⌋ \sum_{p\in\mathbb P}\sum_{d=1}^{\lfloor\frac{min(n,m)}{p}\rfloor}\mu(d)\lfloor \frac n {d\cdot p}\rfloor\lfloor \frac m {d\cdot p}\rfloor p∈P∑d=1∑⌊pmin(n,m)⌋μ(d)⌊d⋅pn⌋⌊d⋅pm⌋
令 D = d ⋅ p D=d\cdot p D=d⋅p,交换一下枚举顺序,即
∑ D = 1 m i n ( n , m ) ∑ p ∣ D , p ∈ P μ ( D p ) ⌊ n D ⌋ ⌊ m D ⌋ \sum_{D=1}^{min(n,m)}\sum_{p|D,p\in\mathbb P}\mu(\frac{D}{p})\lfloor \frac n {D}\rfloor\lfloor \frac m {D}\rfloor D=1∑min(n,m)p∣D,p∈P∑μ(pD)⌊Dn⌋⌊Dm⌋
继续化简,得到
∑ D = 1 m i n ( n , m ) ⌊ n D ⌋ ⌊ m D ⌋ ( ∑ p ∣ D , p ∈ P μ ( D p ) ) \sum_{D=1}^{min(n,m)}\lfloor \frac n {D}\rfloor\lfloor \frac m {D}\rfloor(\sum_{p|D,p\in\mathbb P}\mu(\frac{D}{p})) D=1∑min(n,m)⌊Dn⌋⌊Dm⌋(p∣D,p∈P∑μ(pD))
令 g ( D ) = ∑ p ∣ D , p ∈ P μ ( D p ) g(D)=\sum\limits_{p|D,p\in\mathbb P}\mu(\frac{D}{p}) g(D)=p∣D,p∈P∑μ(pD)。
那么,我们可以先线性筛出 μ \mu μ 的函数值,然后枚举每个质数以及每个质数的倍数把 g g g 暴力更新出来。
剩下的就可以整除分块做了。
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10000005
#define ll long long
using namespace std;
bool mark[N];
int prime[N],mu[N],g[N];
void linear_sieves()
{
int i,j,sum=0;
memset(mark,true,sizeof(mark));
mark[0]=mark[1]=false,mu[1]=1;
for(i=2;i<N;++i)
{
if(mark[i]) prime[++sum]=i,mu[i]=-1;
for(j=1;j<=sum&&i*prime[j]<N;++j)
{
mark[i*prime[j]]=false;
if(i%prime[j]) mu[i*prime[j]]=-mu[i];
else {mu[i*prime[j]]=0;break;}
}
}
for(i=1;i<=sum;++i)
for(j=1;j*prime[i]<N;++j)
g[j*prime[i]]+=mu[j];
for(i=1;i<N;++i) g[i]+=g[i-1];
}
ll solve(int n,int m)
{
int i,j;ll ans=0;
if(n>m) swap(n,m);
for(i=1;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
ans+=1ll*(g[j]-g[i-1])*(n/i)*(m/i);
}
return ans;
}
int main()
{
int n,m,T;
scanf("%d",&T);
linear_sieves();
while(T--)
{
scanf("%d%d",&n,&m);
printf("%lld\n",solve(n,m));
}
return 0;
}