题意
给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对
题解
莫比乌斯反演
还是先上那几个公式
这一种用的比较多 (公式1)
另外一种 (公式2)
设 f(x)为区间内gcd(x,y)==x的对数
则
由公式1知道
其中d是x的倍数
则ans=sigma(f[p]) p为素数
设d=i*p
则f[p]可以化简为
则ans可以化为
然后我们设ip=T
化简得到
一般是先枚举p,然后枚举T,这样时间会超时
我们不妨换一种思考角度,对于每个数T,我们考虑他的素数因子
则可以写成
可以看出来在后面的式子中[N/T]*[M/T]就是个系数项,完全可以提到前面去
所以公式可以写成
后面的那个东西可以用线性筛法解决
具体代码如下
for (int i=0; i<cnt; i++) {
int p=prime[i];
for (ll T=p; T<=n; T+=p) F[T]+=mu[T/p];
}
而前面的那块我们可以通过分块来优化
也就是[N/T]*[M/T]在一段区间内的值是一定的,然后我们只要找出这段区间,设这段区间为[i..j]
则这段区间对答案的贡献为[N/T]*[M/T]*(F[j]-F[i-1])
for(int i=1,j;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
ans+=(F[j]-F[i-1])*(n/i)*(m/i);
}
下面上完整的代码
#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
const int MAXN=10000010;
bool vis[MAXN];
ll mu[MAXN];
ll prime[MAXN];
ll f[MAXN];
ll F[MAXN];
ll n,m;
int T,i,j;
ll read() {
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
} // 读入优化函数
void Prime(int n)
{
int cnt=0;
memset(vis,0,sizeof(vis));
memset(mu,0,sizeof(mu));
mu[1]=1;
for(int i=2;i<n;i++) {
if(!vis[i]) {
prime[cnt++]=i;
mu[i]=-1;
}
for(int j=0;j<cnt&&i*prime[j]<n;j++) {
ll k=i*prime[j];
vis[k]=1;
if(i%prime[j]==0) {
mu[k]= 0;
break;
}
else mu[k]=-mu[i];
}
}
for (int i=0; i<cnt; i++) {
int p=prime[i];
for (ll T=p; T<=n; T+=p) F[T]+=mu[T/p];
}
for (int i=1; i<=n; i++) F[i]+=F[i-1];
}
int main() {
Prime(MAXN-5);
T=read();
while (T--) {
n=read(); m=read();
ll ans=0;
if (n>m) swap(n,m);
for(int i=1,j;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
ans+=(F[j]-F[i-1])*(n/i)*(m/i);
}
printf("%lld\n",ans);
}
}