POJ3090 Visible Lattice Points 【歐拉函數+歐式篩法】

在一個平面直角座標系的第一象限內,如果一個點(x,y)與原點(0,0)的連線中沒有通過其他任何點,則稱該點在原點處是可見的。
例如,點(4,2)就是不可見的,因爲它與原點的連線會通過點(2,1)。
部分可見點與原點的連線如下圖所示:
在這裏插入圖片描述
編寫一個程序,計算給定整數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


歐拉函數

歐拉函數,一般記爲φ(n)φ(n),表示小於等於 n 的數中與 n 互質的數的個數。
如果 n=p1a1×p2a2××pmamn=p_1^{a1}×p_2^{a2}×…×p_m^{am}
φ(n)=n(11p1)(11pm)φ(n)=n(1−\frac 1 p_1)…(1−\frac 1 p_m).

歐拉函數的常用性質

  1. 如果 nn,mm互質,則 φ(nm)=φ(n)φ(m)φ(nm)=φ(n)φ(m);
  2. 小於等於 nn,且與 nn 互質的數的和是 φ(n)×n/2φ(n)×n/2;
  3. 歐拉定理:如果 nn,aa互質,且均爲正整數,則 aφ(n)1(modn)a^{φ(n)}≡1(mod n);

模版
下面的代碼可以在 O(n)O(n) 的時間複雜度內求出 1n1∼n 中所有數的歐拉函數:

int primes[N], euler[N], cnt;
bool st[N];  // st[i]表示i是否被篩過,它最終等於false,表示是質數。

// 質數存在primes[]中,euler[i] 表示
// i的歐拉函數
void get_eulers(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i])
        {
            primes[cnt ++ ] = i;
            euler[i] = i - 1;
        }
        for (int j = 0; j < cnt && i * primes[j] <= n; j ++ )
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0)
            {
                euler[i * primes[j]] = euler[i] * primes[j];
                break;
            }
            euler[i * primes[j]] = euler[i] * (primes[j] - 1);
        }
    }
}

釘子滿足兩個條件:

  1. 除了(1,0),(0,1),(1,1)三個釘子,其他釘子被看到,必須滿足 gcd(x,y)=1gcd(x,y)=1,即橫縱座標互質
  2. 所有釘子滿足沿y=xy=x對稱,即只用考慮一半

綜合以上兩點,對於每個2yN2 \leq y \leq N,需要統計有多少x滿足1x&lt;y1 \leq x \lt y,並且gcd(x,y)=1gcd(x,y)=1
即:x的數量恰好是歐拉函數φ(y)φ(y),最終的答案ans=2×i=2Nφ(i)+3ans=2 \times \sum ^N_{i=2}φ(i) + 3

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1010;

int primes[maxn], phi[maxn], cnt;
bool st[maxn]; //st[i]表示i是否被篩過,它最終等於false,表示是質數。

// O(n)求1~n所有數的歐拉函數
void euler(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i])
        {
            primes[cnt ++ ] = i;
            phi[i] = i - 1;
        }
        for (int j = 0; j < cnt && i * primes[j] <= n; j ++ )
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0)
            {
                phi[i * primes[j]] = phi[i] * primes[j];
                break;
            }
            phi[i * primes[j]] = phi[i] * (primes[j] - 1);
        }
    }
}

int main()
{
    euler(1000);
	for (int i = 3; i <= 1000; i++)
		phi[i] += phi[i - 1];
    
    int q, n;
	cin >> q;
	for (int i = 1; i <= q; i ++)
	{
        cin >> n;
        cout << i << " " << n << " " << phi[n]*2+3 << endl;
	}
    return 0;
}


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