题解——GCD

给定正整数 n,求 1x,yngcd(x,y) 为素数的数对 (x,y) 有多少对。

n107

题解

做法1

题意即为求S=p|ni=1nj=1n[gcd(i,j)=p]

常规操作化简S=p|ni=1npj=1np[gcd(i,j)=1]

如果我们记n=np,f(i)=a=1nb=1n[gcd(a,b)=i],g(i)=i|df(d)

则展开g(i)

g(i)=i|da=1nb=1n[gcd(a,b)=d]

常规操作g(i)=a=1nb=1n[i|gcd(a,b)]=a=1nib=1ni[1|gcd(a,b)]=ni2

所以S=p|nf(p)=p|nd=1dpnμ(d)g(p)=p|nd=1pdnμ(d)ndp2

接着有设T=dp,这里由于p,d具有对称性,交换求和次序有S=T=1nnT2p|Tμ(Tp)

然后T=1nnT2可以整除分块,而后面的可以在筛一遍质数之后O(n)求出,于是复杂度就是O(n),实际上远远跑不满

做法2

事实上,因为数对具有对称性,我们可以将S稍稍变形得到

S=p|ni=1npj=1np[gcd(i,j)=1]=p|n(2i=1npj=1i[gcd(i,j)=1]1)

这个1是因为不能让(p,p)统计两次,根据φ的定义,会发现原式等价于p|n(2i=1npφ(i)1)

预处理欧拉函数前缀和即可线性求解

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 10000005
#define int long long
int n, phi[N], prime[N], v[N], cnt, s[N];
int init() {
	phi[1] = 1;
	scanf("%lld", &n);
	for (int i = 2; i <= n; i++) {
		if (!v[i]) {
			v[i] = i;
			prime[++cnt] = i;
			phi[i] = i - 1;
		}
		for (int j = 1; j <= cnt; j++) {
			if (prime[j] > v[i] || prime[j] * i > n)break;
			v[prime[j] * i] = prime[j];
			phi[prime[j] * i] = i % prime[j] ? phi[i] * (prime[j] - 1) : phi[i] * prime[j];
		}
	}
	for (int i = 1; i <= n; i++) {
		s[i] = s[i - 1] + phi[i];
	}
	return 0;
}
void solve() {
	int m = n, ans = 0;
	for (int i = 1; i <= cnt; i++) {
		ans += s[m / prime[i]] << 1;
		ans--;
	}
	printf("%lld", ans);
	return;
}
signed main() {
	init();
	solve();
	return 0;
}
posted @   spdarkle  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示