ACWING201. 可見的點(歐拉函數)

在一個平面直角座標系的第一象限內,如果一個點(x,y)與原點(0,0)的連線中沒有通過其他任何點,則稱該點在原點處是可見的。

例如,點(4,2)就是不可見的,因爲它與原點的連線會通過點(2,1)。

部分可見點與原點的連線如下圖所示:

3090_1.png

編寫一個程序,計算給定整數N的情況下,滿足0≤x,y≤N的可見點(x,y)的數量(可見點不包括原點)。

輸入格式
第一行包含整數C,表示共有C組測試數據。

每組測試數據佔一行,包含一個整數N。

輸出格式
每組測試數據的輸出佔據一行。

應包括:測試數據的編號(從1開始),該組測試數據對應的N以及可見點的數量。

同行數據之間用空格隔開。

數據範圍
1≤N,C≤1000
輸入樣例:
4
2
4
5
231
輸出樣例:
1 2 5
2 4 13
3 5 21
4 231 32549

思路:
題目可以轉換成,能尋找出多少k=(y2y1)/(x2x1)k = (y2 - y1)/(x2 - x1)
其中y2,x2均爲非負整數,且x1 = y1 = 0。
且易得y2和x2一定是沒有公約數的,否則將其約去公約數後的點一定在其左下被提前看到。
那麼gcd(x2,y2) = 0。
需要特殊考慮的是(1,0),(0,1),(1,1)這三個點。
不妨設y2>x2y2>x2。所以本題轉換成了求2~n內歐拉函數的和,同時這個和要乘以2,再加上一開始的3個點。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 1005;
int v[maxn],phi[maxn],prime[maxn];
int sum[maxn];

void euler()
{
    int cnt = 0;
    for(int i = 2;i <= 1000;i++)
    {
        if(v[i] == 0)
        {
            v[i] = i;
            phi[i] = i - 1;
            prime[++cnt] = i;
        }
        for(int j = 1;j <= cnt && prime[j] * i < maxn;j++)
        {
            v[i * prime[j]] = prime[j];
            phi[i * prime[j]] = phi[i] * (i % prime[j] ? prime[j] - 1 : prime[j]);
            if(i % prime[j] == 0)break;
        }
    }
}

int main()
{
    euler();
    int T;scanf("%d",&T);
    for(int i = 1;i <= 1000;i++)
    {
        sum[i] = sum[i - 1] + phi[i];
    }
    int kase = 0;
    while(T--)
    {
        int n;scanf("%d",&n);
        printf("%d %d %d\n",++kase,n,sum[n] * 2 + 3);
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章