在一個平面直角座標系的第一象限內,如果一個點(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 互質的數的個數。
如果
則 .
歐拉函數的常用性質
- 如果 ,互質,則 ;
- 小於等於 ,且與 互質的數的和是 ;
- 歐拉定理:如果 ,互質,且均爲正整數,則 ;
模版
下面的代碼可以在 的時間複雜度內求出 中所有數的歐拉函數:
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,0),(0,1),(1,1)三個釘子,其他釘子被看到,必須滿足 ,即橫縱座標互質
- 所有釘子滿足沿對稱,即只用考慮一半
綜合以上兩點,對於每個,需要統計有多少x滿足,並且。
即:x的數量恰好是歐拉函數,最終的答案
#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;
}