bzoj2818 Gcd
2818: Gcd
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 5930 Solved: 2636
[Submit][Status][Discuss]
Description
给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的
数对(x,y)有多少对.
Input
一个整数N
Output
如题
Sample Input
4
Sample Output
4
HINT
hint
对于样例(2,2),(2,4),(3,3),(4,2)
1<=N<=10^7
Source
分析:这道题是比较典型的gcd(a,b) = c求个数之类的问题,做法和这道题:传送门差不多,首先我们假设gcd(a,b) = g,这样不能直接统计,那么我们可以得到a = a' * g,b = b' * g, (a',b') = 1,同时约掉g,就变成了求互质的数的个数。不过这和bzoj2705有点不太一样,它并没有一个确定的数,不过我们可以分类讨论.
1.如果x < y,那么从1到maxn枚举y,答案就是Σφ(y).
2.如果x > y,一样的,不过答案就是Σφ(x).
3.如果x = y,那么情况只有一种:x = y = 1,但是这种情况已经在上面两种情况中被算了,所以总的答案就是2*Σφ(i) - 1 (1 <= i <= maxn)
那么这个maxn就很显然了,就是n除以当前的g.
这个题的数据范围有点大,需要用到线性算法,我们既要求出质数,又要推出欧拉函数值,那么直接用线性筛就能解决.
#include <cstdio> #include <queue> #include <cstring> #include <iostream> #include <algorithm> #include <cmath> using namespace std; int n,tot = 0; const int maxn = 10000010; int p[maxn], prime[maxn], f[maxn]; long long sum[maxn]; long long ans; void init() { p[1] = 1; for (int i = 2; i <= n; ++i) { if (!f[i]) { prime[++tot] = f[i] = i; p[i] = i - 1; } for (int j = 1; j <= tot; ++j) { long long t = i * prime[j]; if (t > n) break; f[t] = prime[j]; p[t] = p[i] * (prime[j] - (prime[j] < f[i])); if (prime[j] == f[i]) break; } } } int main() { scanf("%d", &n); init(); for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + p[i]; for (int i = 1; i <= tot; i++) ans += 2 * sum[n / prime[i]] - 1; printf("%lld", ans); return 0; }