【马蹄集】—— 数论专题:质数

数论专题:质数



目录

  • MT2203 约数个数
  • MT2204 约数之和
  • MT2205 模数
  • MT2206 tax
  • MT2207 数树




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 1n109


相关知识点:数论


题解


方法一:暴力求解

求数 n n n 的约数(因数)个数,最简单的办法就是通过一重循环扫描 1 ∼ n 1\sim n 1n 并逐个取余,然后统计所有余数为 0 的情况即可。实际中为了加快扫描速度,通常只会扫描 1 ∼ n 1\sim \sqrt n 1n ,然后在遇到余数为 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;
}

方法二:分解质因数

我们知道,任何一个合数都可以写成若干个质数相乘的形式,其中每个质数都是这个合数的因数,把一个合数用质因数相乘的形式表示出来,叫做分解质因数,也叫做分解质因子。

初中时,我们学过通过短除法的形式来分解一个数的质因数,其过程如下:

【马蹄集】—— 数论专题:质数_第1张图片

因此,可将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 2i3j5k 中任意选择(注: 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=421×31×50=621×31×51=3020×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 λ=13xλ=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 
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 1n106


相关知识点:数论


题解


方法一:暴力求解

暴力求解数 n n n 的约数(因数)之和只需要通过循环遍历 1 ∼ n 1\sim\sqrt n 1n 中的全部数,并将所有的因数进行叠加即可。当然,在叠加时就需要统计 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)=p11p1a11×p21p2a21××pk1pkak1

即:

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)=λ=1kpλ1pλaλ1

所以,采用约束和定理求解此题时,需要首先求出整个数列的质因数及其次数。由于前面已经给出了求解任意正整数的质因数方法,在此就不再赘述。下面直接给出求解本题的完整代码(已 AC):

/*
    MT2204 约数之和
*/
#include 
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 1a,b109


相关知识点:数论


题解


本题讨论 a   m o d   x   =   b a\ mod\ x\ =\ b a mod x = b 成立时, x x x 的取值情况。对于取模运算而言,该式需要讨论的情况实际有三类:

  • a < b aa<b 时,无论 a a a 对何值进行取模,其取值永远都小于 b b b,即 a   m o d   x   < b a\ mod\ x\ 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 (ab) mod x = 0 成立。换言之,要求数 ( a − b ) \left(a-b\right) (ab) 的约数个数。前面提到,对任意正整数 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 ib 时, 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 (ab)=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 
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 n100
对于50%的数据: 2 ≤   n ≤ 10000 2\le\ n\le10000 2 n10000
对于100%的数据: 2 ≤   n ≤ 2 × 10 9 2\le\ n\le{2\times10}^9 2 n2×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,n2} (显然此处的 n − 2 n-2 n2 是一个奇数)。如果 n − 2 n-2 n2 是一个质数,那么最低税费就为 1+1=2;如果 n − 2 n-2 n2 不是一个质数考虑则另一套划分方案;
    • n n n 划分为 { 质数, 偶数 }(注意,除 2 以外的所有质数都是奇数)。那么此时的最低税费为 1+2=3。

基于此,可写出求解本题的完整代码(已 AC):

/*
    MT2206 tax 
    哥德巴赫猜想:任一大于2的偶数都可以表示成二个质数之和 
*/
#include 
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 的树林。

【马蹄集】—— 数论专题:质数_第2张图片

请你计算出在一个 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 0x,y n, 1n2000, 1c10


相关知识点:数论


题解


从图中可以很直观地看出,只要两个点的斜率相等(如果存在),则其中坐标值更大的点必定是不可见的。换言之,对任意坐标 ( 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 
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;
} 

END


你可能感兴趣的:(马蹄集试题题解,MT2203,约数个数,MT2204,约数之和,MT2205,模数,MT2206,tax,MT2207,数树,哥德巴赫猜想,约数和定理)