数论专题:质数
MT2203 约数个数
难度:黄金 时间限制:1秒 占用内存:128M
题目描述
给定正整数 n n n,求 n n n 的约数个数。
格式
输入格式:一个整数 n n n。
输出格式:输出一行一个整数表示答案。样例 1
输入:
12输出:
6备注
其中: 1 ≤ n ≤ 1 0 9 1\le n \le 10^9 1≤n≤109。
相关知识点:
数论
题解
方法一:暴力求解
求数 n n n 的约数(因数)个数,最简单的办法就是通过一重循环扫描 1 ∼ n 1\sim n 1∼n 并逐个取余,然后统计所有余数为 0 的情况即可。实际中为了加快扫描速度,通常只会扫描 1 ∼ n 1\sim \sqrt n 1∼n,然后在遇到余数为 0 的情况时统计 2 个单位的数。因为对任何数 n n n,如果存在 n m o d x = 0 n\mod x=0 nmodx=0,则必定有 n m o d n x = 0 n\mod\frac{n}{x}=0 nmodxn=0 成立。例如,对 12 而言,当 12 m o d 2 = 0 12\mod 2=0 12mod2=0 时,同时有 12 m o d 12 2 = 12 m o d 6 = 0 12\mod\frac{12}{2}=12\mod6=0 12mod212=12mod6=0 。采取这种方法求某个数( n n n)的余数个数时,对 n \sqrt n n 需要注意一点:当 n \sqrt n n 为 n n n 的约数且 n \sqrt n n 为整数时,统计时只计 1 个单位的数。例如,对于数 25,其平方根为 25 = 5 \sqrt{25}=5 25=5,由于 25 5 = 5 \frac{25}{5}=5 525=5 ,还是 5 这个数本身,因此这里实际上只产生一个约数,所以只需要计 1 个数。采取这种方法得到的代码如下:
// 求指定数的约数个数
int getDivisorNum(int n)
{
int divNum = 2, limit = sqrt(n);
// 统计约数个数
for(int i=2; i<=limit; i++)
if(n%i == 0)
divNum += 2;
// 特判
if(limit*limit == n) divNum --;
return divNum;
}
方法二:分解质因数
我们知道,任何一个合数都可以写成若干个质数相乘的形式,其中每个质数都是这个合数的因数,把一个合数用质因数相乘的形式表示出来,叫做分解质因数,也叫做分解质因子。
初中时,我们学过通过短除法的形式来分解一个数的质因数,其过程如下:
因此,可将360用质因数表达为:
360 = 2 3 × 3 2 × 5 1 360=2^3\times3^2\times5^1 360=23×32×51
也就是说,从 2 i 、 3 j 、 5 k 2^i、3^j、5^k 2i、3j、5k 中任意选择(注: i ∈ { 0 , 1 , 2 , 3 } , j ∈ { 0 , 1 , 2 } , k ∈ { 0 , 1 } i\in\left\{0,\ 1,\ 2,\ 3\right\},j\in\left\{0,\ 1,\ 2\right\},k\in\left\{0,\ 1\right\} i∈{0, 1, 2, 3},j∈{0, 1, 2},k∈{0, 1}),并将选出的数相乘得到的数都是 360 的因数。例如: 2 2 × 3 0 × 5 0 = 4 、 2 1 × 3 1 × 5 0 = 6 、 2 1 × 3 1 × 5 1 = 30 、 2 0 × 3 2 × 5 1 = 45 2^2\times3^0\times5^0=4、2^1\times3^1\times5^0=6、2^1\times3^1\times5^1=30、2^0\times3^2\times5^1=45 22×30×50=4、21×31×50=6、21×31×51=30、20×32×51=45 都可以被 360 整除。
那由这些数可以构成多少种选数组合呢?这是一个排列组合问题:第 λ \lambda λ 个集合中有 x λ x_\lambda xλ 种取法,问总的取法有多少种?答案是 ∏ λ x λ \prod_{\lambda} x_\lambda ∏λxλ 种。例如,上面的例子中,总的组合方案数为:
∏ λ = 1 3 x λ = 4 × 3 × 2 = 24 \prod_{\lambda=1}^{3}x_\lambda=4\times3\times2=24 λ=1∏3xλ=4×3×2=24
所以现在的我们的任务就是如何分解质因数,并统计各质因数的次数?
这个任务很简单,我们只需要模拟短除法即可,具体实现如下:
// 通过短除法获取一个数的质因数
void shortDivision(int n){
// 保存全部的质因数
queue<int> q;
for(int i=2; i*i<=n; i++){
// 当前数为数 n 的因数时,要不断用该数进行分解
while(n%i==0){
// 加入队列
q.push(i);
// 对数 n 进行分解
n /= i;
}
}
// 如果分解得到的最后结果不为 1 ,则最终状态的 n 也是原数的因数
if(n!=1) q.push(n);
}
在上面的代码中,系统会从最小的质因数 2 开始向后遍历,一旦确定某个数 i i i 是传入的数 n n n 的质因数后,会通过一层循环不断地对原数 n n n 按 i i i 进行分解。例如,当 n = 360 n=360 n=360 时,会对 i = 2 i=2 i=2 执行以下步骤:
- n%2=360%2=0,则 q.push(2),且 n = n 2 = 180 n=\frac{n}{2}=180 n=2n=180(此时队列 q={2})。
- n%2=180%2=0,则 q.push(2),且 n = n 2 = 90 n=\frac{n}{2}=90 n=2n=90(此时队列 q={2, 2})。
- n%2=90%2=0,则 q.push(2),且 n = n 2 = 45 n=\frac{n}{2}=45 n=2n=45(此时队列 q={2, 2, 2})。
这个过程不仅统计了质因数 i i i 的次数,还对外层循环进行了缩减(从360→180→90→45)。
接下来由于 45%2≠0 ,故退出 while 循环,并将 i i i 从 2 变为 3( 3 × 3 = 9 < 45 3\times3=9<45 3×3=9<45),接着执行以下步骤:
- n%3=45%3=0,则 q.push(3),且 n = n 3 = 15 n=\frac{n}{3}=15 n=3n=15(此时队列 q={2, 2, 2, 3})。
- n%3=15%3=0,则 q.push(3),且 n = n 3 = 5 n=\frac{n}{3}=5 n=3n=5(此时队列 q={2, 2, 2, 3, 3})。
接下来由于 5%3≠0,故 i i i 从 3 变为 4。但对外层循环控制条件而言,由于 4 × 4 = 16 > 5 4\times4=16>5 4×4=16>5,故退出。
此时观察原数 n n n 的最终取值,由于该值不为 1,说明这个数也是原数 n n n (360)的一个质因数,因此将其加入队列中(此时队列 q={2, 2, 2, 3, 3, 5})。
最终算法结束,队列 q 中保存的即为给定数 n n n 的全部质因数。
对本题而言,我们不需要单独设置数据结构去存储全部质因数,而只需要统计每一个质因数的出现次数,并将这些次数进行叠乘即可。因此,可得到求解本题的完整代码:
/*
MT2203 约数个数
*/
#include<bits/stdc++.h>
using namespace std;
// 求指定数的约数个数(利用短除法求出所有质约数)
int getDivisorNum(int n)
{
int divSum = 1, tmp;
for(int i=2; i*i<=n; i++){
tmp = 1;
// 统计质因数个数
while(n%i==0){
tmp++;
n /= i;
}
// 对约数总数结果进行统计
divSum *= tmp;
}
// 短除法结束后,剩下的数如果不是 1,还需要对其进行统计
if(n != 1) divSum *= 2;
return divSum;
}
int main( )
{
// 获取输入
int n; cin>>n;
// 输出约数个数
cout<<getDivisorNum(n)<<endl;
return 0;
}
MT2204 约数之和
难度:黄金 时间限制:1秒 占用内存:128M
题目描述
给定正整数 n n n,求 n n n 的约数之和。
格式
输入格式:一个整数 n n n。
输出格式:输出一行一个整数表示答案。样例 1
输入:
12输出:
28备注
其中: 1 ≤ n ≤ 1 0 6 1\le n \le 10^6 1≤n≤106。
相关知识点:
数论
题解
方法一:暴力求解
暴力求解数 n n n 的约数(因数)之和只需要通过循环遍历 1 ∼ n 1\sim\sqrt n 1∼n 中的全部数,并将所有的因数进行叠加即可。当然,在叠加时就需要统计 x x x 和 n x \frac{n}{x} xn 两个值( x x x 为因数)。同样地,对 int ( n ) \text{int}\left(\sqrt n\right) int(n) 需要进行特判,一旦 int ( n ) × int ( n ) \text{int}\left(\sqrt n\right)\times\text{int}\left(\sqrt n\right) int(n)×int(n) (即 n \sqrt n n 为整数时),就只需要算一个数。下面给出暴力求解的算法:
// 求指定数的约数之和
int getDivisorSum(int n)
{
int divSum = 1+n, limit = sqrt(n);
// 统计约数之和
for(int i=2; i<=limit; i++)
if(n%i == 0)
divSum += (i + n/i);
// 特判
if(limit*limit == n) divSum -= limit;
return divSum;
}
方法二:约数和定理
数 n n n 的约数(因数)之和实际上涉及到数论相关知识(约束和定理)。
前面我们曾提到,任意大于 1 的正整数 n n n 均可被分解为若干个质因数之积:
n = p 1 a 1 × p 2 a 2 × ⋯ × p k a k n=p_1^{a_1}\times p_2^{a_2}\times\dots\times p_k^{a_k} n=p1a1×p2a2×⋯×pkak
则其中 p i a i p_i^{a_i} piai 可生成的约数有 p i 0 , p i 1 , ⋯ , p i a i p_i^0,\ p_i^1,\ \cdots,\ p_i^{a_i} pi0, pi1, ⋯, piai 共 ( a i + 1 ) \left(a_i+1\right) (ai+1) 个。
而从约数的定义可知,约数是在 p 1 a 1 , p 2 a 2 , ⋯ , p k a k p_1^{a_1},p_2^{a_2},\cdots,p_k^{a_k} p1a1,p2a2,⋯,pkak 中分别挑选一个相乘得到,因此总的挑选方法有:
d ( n ) = ( 1 + a 1 ) + ( 1 + a 2 ) + ⋯ + ( 1 + a k ) d\left(n\right)=\left(1+a_1\right)+\left(1+a_2\right)+\dots+\left(1+a_k\right) d(n)=(1+a1)+(1+a2)+⋯+(1+ak)
由乘法原理可知,他们的总和就是这些情况分别相乘得到,即:
f ( n ) = ( p 1 0 + p 1 1 ⋯ + p 1 a 1 ) × ( p 2 0 + p 2 1 ⋯ + p 2 a 2 ) × ⋯ × ( p k 0 + p k 1 ⋯ + p k a k ) f\left(n\right)=\left(p_1^0+p_1^1\cdots+p_1^{a_1}\right)\times\left(p_2^0+p_2^1\cdots+p_2^{a_2}\right)\times\cdots\times\left(p_k^0+p_k^1\cdots+p_k^{a_k}\right) f(n)=(p10+p11⋯+p1a1)×(p20+p21⋯+p2a2)×⋯×(pk0+pk1⋯+pkak)
对每一项 ( p i 0 + p i 1 ⋯ + p i a 2 ) \left(p_i^0+p_i^1\cdots+p_i^{a_2}\right) (pi0+pi1⋯+pia2),都是一个首项为 1,等比为 p i p_i pi 的等比数列,于是可将上式化简为:
f ( n ) = p 1 a 1 − 1 p 1 − 1 × p 2 a 2 − 1 p 2 − 1 × ⋯ × p k a k − 1 p k − 1 f\left(n\right)=\frac{p_1^{a_1}-1}{p_1-1}\times\frac{p_2^{a_2}-1}{p_2-1}\times\cdots\times\frac{p_k^{a_k}-1}{p_k-1} f(n)=p1−1p1a1−1×p2−1p2a2−1×⋯×pk−1pkak−1
即:
f ( n ) = ∏ λ = 1 k p λ a λ − 1 p λ − 1 f\left(n\right)=\prod_{\lambda=1}^{k}\frac{p_\lambda^{a_\lambda}-1}{p_\lambda-1} f(n)=λ=1∏kpλ−1pλaλ−1
所以,采用约束和定理求解此题时,需要首先求出整个数列的质因数及其次数。由于前面已经给出了求解任意正整数的质因数方法,在此就不再赘述。下面直接给出求解本题的完整代码(已 AC):
/*
MT2204 约数之和
*/
#include<bits/stdc++.h>
using namespace std;
// 求指定数的约数之和 (利用短除法求出所有质约数)
int getDivisorSum(int n)
{
int divSum = 1, tmp;
for(int i=2; i*i<=n; i++){
tmp = i;
// 统计质因数个数
while(n%i==0){
tmp *= i;
n /= i;
}
// 对约数和进行累乘统计
divSum *= (tmp-1)/(i-1);
}
// 短除结束后,剩下的数如果不是 1,还需要对其进行统计
if(n != 1) divSum *= (n+1);
return divSum;
}
int main( )
{
// 获取输入
int n; cin>>n;
// 输出约数个数
cout<<getDivisorSum(n)<<endl;
return 0;
}
MT2205 模数
难度:黄金 时间限制:1秒 占用内存:128M
题目描述
给定两个整数 a , b a, b a,b,问有多少个 x x x 使得等式 a m o d x = b a\ mod\ x\ =\ b a mod x = b 成立。如果存在无数个就输出
infinity
,否则输出满足条件的 x x x 的个数。格式
输入格式:两个整数 a , b a, b a,b。
输出格式:输出个数或infinity
。样例 1
输入:
21 5输出:
2备注
其中: 1 ≤ a , b ≤ 1 0 9 1\le a, b \le 10^9 1≤a,b≤109。
相关知识点:
数论
题解
本题讨论 a m o d x = b a\ mod\ x\ =\ b a mod x = b 成立时, x x x 的取值情况。对于取模运算而言,该式需要讨论的情况实际有三类:
- 当 a < b a<b a<b 时,无论 a a a 对何值进行取模,其取值永远都小于 b b b,即 a m o d x < b a\ mod\ x\ <b a mod x <b。因此这种情况下满足 a m o d x = b a\ mod\ x\ =\ b a mod x = b 的 x x x 数量为 0。如 3 对任意数取模都不可能得到 6;
- 当 a = b a=b a=b 时,对任意大于 a a a 的数 x x x ,都存在 a m o d x = b a\ mod\ x\ =\ b a mod x = b。因此这种情况下满足 a m o d x = b a\ mod\ x\ =\ b a mod x = b 的 x x x 数量具有无限个。如 3 对任意比 3 大的数取模得到的结果都为 3;
- 当 a > b a>b a>b 时。要使 a m o d x = b a\ mod\ x\ =\ b a mod x = b 成立,实际上就是要使等式 ( a − b ) m o d x = 0 \left(a-b\right)\ mod\ x\ =\ 0 (a−b) mod x = 0 成立。换言之,要求数 ( a − b ) \left(a-b\right) (a−b) 的约数个数。前面提到,对任意正整数 n n n,如果存在数 i i i 为其约数,则数 n i \frac{n}{i} in 也为其约数。对本题而言,这里对约数 i i i 还存在一个额外限制: i > b i>b i>b (当 i ≤ b i\le b i≤b 时, a m o d i a\mod i amodi 的结果永远比 b b b 小)。例如,当 a = 15 , b = 3 a=15,\ b=3 a=15, b=3 时,数 ( a − b ) = 12 \left(a-b\right)=12 (a−b)=12 的约数有 2,3,4 共三个。但使 a m o d x = b a\ mod\ x\ =\ b a mod x = b 成立的 x x x 实际上只有 4 一个,因为只有 4 是大于 b = 3 b=3 b=3 的,而 15 m o d 2 = 1 , 15 m o d 3 = 0 , 15 m o d 4 = 3 15\mod2=1, 15\mod3=0, 15\mod4=3 15mod2=1,15mod3=0,15mod4=3。
因此,本题实际上也是在考察约数,不过是具有特殊限制的约数。由于前面已经给出了求约数的具体算法,故下面直接给出求解本题的完整代码(已 AC):
/*
MT2205 模数
*/
#include<bits/stdc++.h>
using namespace std;
// 求能满足两个指定数的模数个数
// 即:a mod x = b 的 x 解个数
int getModnum(int a, int b)
{
if(a < b) return 0;
if(a == b) return -1;
a -= b;
int modnum = 0, limit = sqrt(a);
for(int i=1; i<=limit; i++)
if(a%i == 0){
if(i > b) modnum++;
if(a/i > b) modnum++;
}
// 特判
if(limit*limit == a && a/limit > b) modnum--;
return modnum;
}
int main( )
{
// 获取输入
int a, b; cin>>a>>b;
// 输出约数个数
int ans = getModnum(a, b);
if(ans<0) cout<<"infinity"<<endl;
else cout<<ans<<endl;
return 0;
}
MT2206 tax
难度:钻石 时间限制:1秒 占用内存:128M
题目描述
小码哥要交税,交的税钱是收入 n n n 的最大因子(该最大因子为不等于 n n n 的最大因子),但是现在小码哥为了避税,把钱拆成几份(每份至少为 2),使交税最少,输出税钱。
格式
输入格式:一个正整数 n n n 表示收入(总钱数)。
输出格式:输出一个正整数表示税钱。样例 1
输入:
4输出:
2备注
对于30%的数据: 2 ≤ n ≤ 100 2\le\ n\le100 2≤ n≤100;
对于50%的数据: 2 ≤ n ≤ 10000 2\le\ n\le10000 2≤ n≤10000;
对于100%的数据: 2 ≤ n ≤ 2 × 10 9 2\le\ n\le{2\times10}^9 2≤ n≤2×109。
相关知识点:
数论
题解
首先对题目进行解读。
现要求对一个数 n n n 进行拆分,你可以将其任意分为若干份(但每份的值不能低于 2)。接下来对每一份,取该数值的最大因数(不包括自己,下同)为税钱,然后统计总税钱,并使该税钱尽可能少。例如当 n = 8 n=8 n=8 时,有以下四种拆分方式:
- 分为 1 份,即{8}。由于 8 的最大因数为 4,因此这种情况要交的税钱为 4;
- 分为 2 份。
- 拆分为 {2, 6}。由于 2、6 的最大因数分别为 1、3,因此这种情况要交的税钱为 1+3=4;
- 拆分为 {3, 5}。由于 3、5 的最大因数均为 1,因此这种情况要交的税钱为 1+1=2;
- 拆分为 {4, 4}。由于 4 的最大因数为 2,因此这种情况要交的税钱为 2+2=4;
- 分为 3 份,即 {2, 3, 3}。由于 2、3 的最大因数均为 1,因此这种情况要交的税钱为 1+1+1=3;
- 分为 4 份,即 {2, 2, 2, 2}。由于 2 的最大因数均为 1,因此这种情况要交的税钱为 1+1+1+1=4。
从上面的拆分结果来看,显然将 8 分为 {3, 5} 能使最终交的税最少。在这情况下,8 被分为两个质数,因此他们的最大因数均为 1,于是最终的税费即为被划分的份数。
本题的任务就是输入一个数 n n n ,然后输出能够缴纳的最少税费。
从上面的例子可以看出,要想得到最少税费,那一定要想办法将数 n n n 划分为两个质数之和。于是乎,这就不得不提到世界近代三大数学难题之一——哥德巴赫猜想。
哥德巴赫于1742年在给欧拉的信中提出了以下猜想:任一大于2的整数都可写成三个质数之和。但是哥德巴赫自己无法证明它,于是就写信请教赫赫有名的大数学家欧拉帮忙证明,但是一直到死,欧拉也无法证明。1966年陈景润证明了“1+2”成立,即
任一充分大的偶数都可以表示成二个素数的和
。
于是,我们可以对原问题进行以下分类讨论:
- 当 n n n 为质数时(注意 2 也是质数),最低税费就是 1(不进行任何划分);
- 当 n n n 为(大于 2 的)偶数时,根据哥德巴赫猜想可知,其最低税费为 2;
- 当
n
n
n 为奇数时,由于任意奇数都可以被划分为一个奇数和一个偶数之和,而对偶数而言,其最低税费只能是两种情况:当该偶数为 2 时,最低税费为 1;否则,最低税费为 2。因此为了使税费尽可能低,接下来就只需要考虑两种划分情况:
- 将 n n n 划分为 { 2 , n − 2 } \left\{2,n-2\right\} {2,n−2} (显然此处的 n − 2 n-2 n−2 是一个奇数)。如果 n − 2 n-2 n−2 是一个质数,那么最低税费就为 1+1=2;如果 n − 2 n-2 n−2 不是一个质数考虑则另一套划分方案;
- 将 n n n 划分为 { 质数, 偶数 }(注意,除 2 以外的所有质数都是奇数)。那么此时的最低税费为 1+2=3。
基于此,可写出求解本题的完整代码(已 AC):
/*
MT2206 tax
哥德巴赫猜想:任一大于2的偶数都可以表示成二个质数之和
*/
#include<bits/stdc++.h>
using namespace std;
// 判断一个数是否为质数
bool isPrime(int n)
{
int limit = sqrt(n);
for(int i=2;i<=limit;i++)
if(n%i==0)
return false;
return true;
}
// 根据收入确定税钱
int getTax(int n)
{
// 如果收入是质数则税钱为 1
if(isPrime(n)) return 1;
// 如果收入是偶数(大于2),则税钱为 2(哥德巴赫猜想)
if((n&1)==0) return 2;
// 如果收入是奇数,由于任何奇数都可以拆分为 奇数+偶数 之和
// 则只需要判断该数拆分为 奇数+2 时,奇数是否为质数,则最低税钱为 2
else if(isPrime(n-2)) return 2;
// 否则就将该数拆分为 质数+偶数,则最低税钱为 3(注:除 2 以外的所有质数一定是奇数)
else return 3;
}
int main( )
{
// 获取输入
int n; cin>>n;
// 输出税钱
cout<<getTax(n)<<endl;
return 0;
}
MT2207 数树
难度:黄金 时间限制:1秒 占用内存:128M
题目描述
在卡兹戴尔有一片很奇怪的森林,在一个直角坐标系内的 ( x , y ) \left(x,y\right) (x,y) 坐标值都为自然数的坐标上都有一颗树,如果一棵树的坐标 ( x , y ) \left(x,y\right) (x,y) 与原点 ( 0 , 0 ) \left(0,\ 0\right) (0, 0) 的连线中没有通过其他任何树,则称该树在原点处是可见的。
例如,树 ( 4 , 2 ) \left(4,\ 2\right) (4, 2) 就是不可见的,因为它与原点的连线会通过树 ( 2 , 1 ) \left(2,\ 1\right) (2, 1)。
部分可见点与原点的连线如下图所示,如图是一个 4 × 4 4\times4 4×4 的树林。请你计算出在一个 n × n n\times n n×n 的树林中可见的树有多少。
格式
输入格式:第一行为一个整数 c c c,表示共有 c c c 组测试数据,每组测试数据占一行,为整数 n n n。
输出格式:每组测试数据的输出占据一行。分别为测试数据的编号(从 1 开始),该组测试数据对应的 n n n 以及可见点的数量(同行数据之间用空格隔开)。样例 1
输入:
4
2
4
5
231输出:
1 2 5
2 4 13
3 5 21
4 231 32549备注
其中: 0 ≤ x , y ≤ n , 1 ≤ n ≤ 2000 , 1 ≤ c ≤ 10 0\le x,y\le\ n,\ 1\le n\le2000,\ 1\le c\le10 0≤x,y≤ n, 1≤n≤2000, 1≤c≤10。
相关知识点:
数论
题解
从图中可以很直观地看出,只要两个点的斜率相等(如果存在),则其中坐标值更大的点必定是不可见的。换言之,对任意坐标 ( x , y ) \left(x,y\right) (x,y) ,如果 x , y x,y x,y 的最大公约数不为 1,就说明存在比这个点更小的(整数倍放缩)点 ( x 0 , y 0 ) \left(x_0,y_0\right) (x0,y0)(即有 y x = λ y 0 x 0 \frac{y}{x}=\lambda\frac{y_0}{x_0} xy=λx0y0 成立,其中 λ \lambda λ 为大于 1 的正整数),那么 ( x , y ) \left(x,y\right) (x,y) 就是不可见的。
所以,这道题依然考察了约数的相关内容。具体解法也很简单,遍历全部数据点 ( x , y ) \left(x,y\right) (x,y),所有 x x x 与 y y y 的最大公约数为 1 的点都是可见的,将其进行计数即可。此外,考虑到题目给出的是一个 n × n n\times n n×n 的方阵(具有对称性),因此我们只需要遍历其中的三角阵,在计数时统计 2 个即可,下面给出求解本题的完整代码:
/*
MT2207 数树
*/
#include<bits/stdc++.h>
using namespace std;
// 求两个数的最大公约数
int gcd(int a, int b)
{ return b==0?a:gcd(b, a%b); }
// 对规格为 n×n 的树林进行遍历,查找可见树的数量
int getVisibleTrees(int n)
{
int vt = 2;
for(int i=1; i<=n; i++){
for(int j=1; j<i; j++)
if(gcd(i,j) == 1)
vt += 2;
}
return vt+1;
}
int main( )
{
// 获取输入
int c, n, vt; cin>>c;
for(int i=1; i<=c; i++){
cin>>n;
cout<<i<<" "<<n<<" "<<getVisibleTrees(n)<<endl;
}
return 0;
}