【洛谷P2257】YY的GCD

题目

题目链接:https://www.luogu.com.cn/problem/P2257
给定 \(n,m\),求 \(\gcd(a,b)\) 为质数且 \(1\leq a\leq n,1\leq b\leq m\) 的 \((a,b)\) 有多少对。

思路

经典的莫比乌斯反演模板题。
设 \(f(i)\) 表示 \(\gcd(a,b)=i\) 的方案数,设 \(F(i)\) 表示 \(\gcd(a,b)\) 为 \(i\) 的倍数的方案数。其中 \(i\) 为质数。
那么有

\[F(i)=\left \lfloor \frac{n}{i} \right \rfloor\left \lfloor \frac{m}{i} \right \rfloor \]

\[f(i)=\sum^{\min(n,m)}_{i|d}\mu(\frac{d}{i})F(i)=\sum^{\min(n,m)}_{i|d}\mu(\frac{d}{i})\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor \]

那么

\[ans=\sum^{min(n,m)}_{p\in \operatorname{prm}}\sum^{\min(n,m)}_{p|d}\mu(\frac{d}{p})\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor \]

\[=\sum^{min(n,m)}_{d=1}\left ( \sum^{\min(n,m)}_{p|d,p\in\operatorname{prm}} \mu(\frac{d}{p})\right )\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor \]

那么直接线性筛出 \(\mu\),然后利用埃氏筛思想与处理出 \(\sum^{\min(n,m)}_{p|d,p\in\operatorname{prm}} \mu(\frac{d}{p})\) 以及其前缀和。
对于每一次询问,对 \(\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor\) 整除分块,然后乘上相应前缀和即可。
时间复杂度 \(O(n\log \log n+T\sqrt{n})\)。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=10000010;
int Q,cnt,n,m,prm[N],mu[N],sum[N];
ll ans;
bool v[N];

void findprm(int n)
{
	mu[1]=1;
	for (int i=2;i<=n;i++)
	{
		if (!v[i])
		{
			prm[++cnt]=i;
			mu[i]=-1;
		}
		for (int j=1;j<=cnt;j++)
		{
			if (i>n/prm[j]) break;
			v[prm[j]*i]=1; mu[prm[j]*i]=-mu[i];
			if (!(i%prm[j]))
			{
				mu[prm[j]*i]=0;
				break;
			}
		}
	}
}

int main()
{
	findprm(N-10);
	for (int i=1;i<=cnt;i++)
		for (int j=prm[i];j<N;j+=prm[i])
			sum[j]+=mu[j/prm[i]];
	for (int i=1;i<N;i++)
		sum[i]+=sum[i-1];
	scanf("%d",&Q);
	while (Q--)
	{
		scanf("%d%d",&n,&m);
		ans=0;
		for (int l=1,r;l<=min(n,m);l=r+1)
		{
			int d1=n/l,d2=m/l;
			r=min(n/d1,m/d2);
			ans+=1LL*(sum[r]-sum[l-1])*d1*d2;
		}
		printf("%lld\n",ans);
	}
	return 0;
}
上一篇:最小公倍数的和


下一篇:luogu P3327 [SDOI2015]约数个数和