bzoj2818 Gcd

2818: Gcd

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 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;
}

 

posted @ 2017-08-18 11:16  zbtrs  阅读(156)  评论(0编辑  收藏  举报