当前位置: 首页 > news >正文

CSP-J 2021 入门级 第一轮(初赛) 阅读程序(3)

【题目】

CSP-J 2021 入门级 第一轮(初赛) 阅读程序(3)

01 #include<iostream>
02 using namespace std;03
03
04 const int n = 100000;
05 const int N = n + 1;
06
07 int m;
08 int a[N], b[N], c[N], d[N];
09 int f[N], g[N];
10
11 void init()
12 {
13     f[1]= g[1]= 1;
14     for(int i = 2; i <= n; i++){
15         if(!a[i]) { 
16             b[m++] = i;
17             c[i] = 1, f[i] = 2;
18             d[i] = 1, g[i] = i + 1;
19         }
20         for(int j = 0; j < m && b[j] * i <= n; j++) {
21             int k = b[j];
22             a[i * k] = 1;
23             if(i % k == 0) {
24                 c[i * k] = c[i]+ 1;
25                 f[i * k] = f[i] / c[i * k]*(c[i * k]+ 1);
26                 d[i * k] = d[i];
27                 g[i * k] = g[i] * k + d[i];
28                 break;
29             }
30             else {
31                 c[i * k] = 1;
32                 f[i * k] = 2 * f[i];
33                 d[i * k] = g[i];
34                 g[i * k]= g[i] * (k + 1);
35             }
36          }
37     }
38 }
39
40 int main()
41 {
42     init();
43     
44     int x;
45     cin >> x;
46     cout << f[x] << ' ' << g[x]<< endl;
47     return 0;
48 }

假设输入的x是不超过1000的自然数,完成下面的判断题和单选题:

  • 判断题
  1. 若输入不为1,把第13行删去不会影响输出的结果。( )
  2. 第25行的 f[i] / c[i * k]可能存在无法整除而向下取整的情况。 ( )
  3. 在执行完 init() 后,f 数组不是单调递增的,但 g 数组是单调递增的。 ( )
  • 单选题
  1. init 函数的时间复杂度为( )。
    A. O(n)O(n)O(n)
    B. O(nlog⁡n)O(n\log n)O(nlogn)
    C. O(nn)O(n\sqrt{n})O(nn)
    D. O(n2)O(n^2)O(n2)
  2. 在执行完 init() 后,f[1],f[2],f[3],…,f[100] 中有()个等于 2。
    A. 23
    B. 24
    C. 25
    D. 26
  3. 当输入为 1000 时,输出为()。
    A. 15 1340
    B. 15 2340
    C. 16 2340
    D. 16 1340

【题目考点】

1. 线性筛
2. 算术基本定理

算术基本定理,又称为正整数的唯一分解定理:
任何一个正整数都可以分解成一些质因数的乘积,并且在不计次序的情况下,这种分解方式是唯一的。
N=p1a1p2a2...pnanN = p_1^{a_1}p_2^{a_2}...p_n^{a_n}N=p1a1p2a2...pnan
其中p1<p2<...<pnp_1<p_2<...<p_np1<p2<...<pn且都为质数。
a1,a2,...,ana_1, a_2, ..., a_na1,a2,...,an都是正整数。
正整数N的约数个数为:(a1+1)(a2+1)...(an+1)=∏i=1n(ai+1)(a_1+1)(a_2+1)...(a_n+1) = \prod_{i=1}^n(a_i+1)(a1+1)(a2+1)...(an+1)=i=1n(ai+1)
正整数N的约数和为:(1+p1+p12+...+p1a1)(1+p2+...+p2a2)...(1+pn+...+pnan)=∏i=1n∑j=0aipij(1+p_1+p_1^2+...+p_1^{a_1})(1+p_2+...+p_2^{a_2})...(1+p_n+...+p_n^{a_n})=\prod_{i=1}^n{\sum_{j=0}^{a_i}{p_i^j}}(1+p1+p12+...+p1a1)(1+p2+...+p2a2)...(1+pn+...+pnan)=i=1nj=0aipij

【解题思路】

要想理解本题,首先要会线性筛。(见信息学奥赛一本通 2040:【例5.7】筛选法找质数)
这是线性筛是模板代码:

bool isPrime[1005];//isPrime[i]:i是否是质数 
int prime[1005], pn;//prime[i]:第i个质数 
void initPrime(int n)
{memset(isPrime, 1, sizeof(isPrime));for(int i = 2; i <= n; ++i){if(isPrime[i])prime[++pn] = i;for(int j = 1; j <= pn && i*prime[j] <= n; ++j){isPrime[i*prime[j]] = false;if(i%prime[j] == 0)break;}}
}

对比可知a[i]表示i是否是合数。如果a[i]为0,那么i是质数,如果a[i]为1,那么i是合数。
b数组保存所有筛出的质数,m是b数组中元素的个数,只要i的质数,就将i填充到数组b中。b数组下标从0到m-1保存填充进来的m个质数。
线性筛过程中,每个数都是通过其最小质因数被筛掉。kb[j],即第j个质数。确定i*k是合数时,ki*k的最小质因数。
接下来就有些难懂了。
观察可知,f数组与c数组有关,d数组与g数组有关。
我们首先关注f与c数组,将代码中其它数组相关的内容都去掉:

11 void init()
12 {
13     f[1]= 1;
14     for(int i = 2; i <= n; i++){
15         if(!a[i]) { 
16             b[m++] = i;
16             c[i] = 1, f[i] = 2;
19         }
20         for(int j = 0; j < m && b[j] * i <= n; j++) {
21             int k = b[j];
22             a[i * k] = 1;
23             if(i % k == 0) {
24                 c[i * k] = c[i]+ 1;
25                 f[i * k] = f[i] / c[i * k]*(c[i * k]+ 1);
28                 break;
29             }
30             else {
31                 c[i * k] = 1;
32                 f[i * k] = 2 * f[i];35             }
36          }
37     }
38 }
  • iii为质数时,ci=1c_i = 1ci=1fi=2f_i = 2fi=2
  • 如果iii不是kkk的倍数,那么ci⋅k=1c_{i\cdot k} = 1cik=1fi⋅k=2fif_{i\cdot k} = 2f_ifik=2fi
  • 如果iiikkk的倍数,那么ci⋅k=ci+1c_{i\cdot k} = c_i+1cik=ci+1fi⋅k=fi(ci⋅k+1)ci⋅kf_{i\cdot k} = \dfrac{f_i(c_{i\cdot k}+1)}{c_{i\cdot k}}fik=cikfi(cik+1)
    由于fi⋅kf_{i\cdot k}fik的表达式中的ci⋅kc_{i\cdot k}cik的值就是刚刚赋值的ci+1c_i+1ci+1,因此:
    fi⋅k=fici⋅k+1ci+1f_{i\cdot k} = f_i\dfrac{c_{i\cdot k}+1}{c_{i}+1}fik=fici+1cik+1

到这里,如果想看出ccc数组与f数组的意义,必须对算术基本定理十分熟悉。(见上面【题目考点】)。

fi⋅kf_{i\cdot k}fik每次乘以ci⋅k+1c_{i\cdot k}+1cik+1,和求一个正数的约数个数过程中,每次乘以质因数的指数加1,即(an+1)(a_n+1)(an+1),很相似。进而想到ccc应该表示的是质因数的指数。
iiikkk的倍数时,kkki⋅ki\cdot kik的最小质因数,kkk也是iii的最小质因数。
那么i⋅ki\cdot kik分解质因数后kkk的指数比iii分解质因数后kkk的指数大1。
因此可以联想到:cic_ici表示iii分解质因数后,iii的最小质因数的指数。

cic_ici表示iii分解质因数后,iii的最小质因数的指数。

  • iii为质数时,iii的最小质因数是自己,指数为1,因此ci=1c_i = 1ci=1
  • iii不是kkk的倍数时,kkki⋅ki\cdot kik的最小质因数,kkk的指数为1,因此ci⋅k=1c_{i\cdot k} = 1cik=1
  • iiikkk的倍数时,kkki⋅ki\cdot kikiii的最小质因数,i⋅ki\cdot kik分解质因数后kkk的指数比iii分解质因数后kkk的指数大1,因此有ci⋅k=ci+1c_{i\cdot k} = c_{i}+1cik=ci+1

i=kcip1a1...pnani = k^{c_i}p_1^{a_1}...p_n^{a_n}i=kcip1a1...pnan
那么i⋅k=kci⋅kp1a1...pnani\cdot k= k^{c_{i\cdot k}}p_1^{a_1}...p_n^{a_n}ik=kcikp1a1...pnan
iii的约数个数为(ci+1)(a1+1)...(an+1)(c_i+1)(a_1+1)...(a_n+1)(ci+1)(a1+1)...(an+1)
i⋅ki\cdot kik的约数个数为(ci⋅k+1)(a1+1)...(an+1)(c_{i\cdot k}+1)(a_1+1)...(a_n+1)(cik+1)(a1+1)...(an+1)
iii的约数个数除以(ci+1)(c_i+1)(ci+1)再乘以(ci⋅k+1)(c_{i\cdot k}+1)(cik+1),就能得到i⋅ki\cdot kik的约数个数。
据此可知,fif_ifi表示iii的约数个数

fif_ifi表示iii的约数个数

  • iii是质数时,iii的约数有2个,因此fi=2f_i=2fi=2
  • iii不是kkk的倍数时,kkki⋅ki\cdot kik的最小质因数
    i=p1a1...pnani = p_1^{a_1}...p_n^{a_n}i=p1a1...pnan,约数个数为:fi=(a1+1)...(an+1)f_i=(a_1+1)...(a_n+1)fi=(a1+1)...(an+1)
    那么i⋅k=kp1a1...pnani\cdot k= kp_1^{a_1}...p_n^{a_n}ik=kp1a1...pnan,约数个数为:fi⋅k=(1+1)(a1+1)...(an+1)f_{i\cdot k} = (1+1)(a_1+1)...(a_n+1)fik=(1+1)(a1+1)...(an+1)
    因此fi⋅k=2fif_{i\cdot k} = 2f_ifik=2fi
  • iiikkk的倍数时,kkki⋅ki\cdot kik的最小质因数
    i=kcip1a1...pnani = k^{c_i}p_1^{a_1}...p_n^{a_n}i=kcip1a1...pnan
    那么i⋅k=kci⋅kp1a1...pnani\cdot k= k^{c_{i\cdot k}}p_1^{a_1}...p_n^{a_n}ik=kcikp1a1...pnan
    iii的约数个数为fi=(ci+1)(a1+1)...(an+1)f_i=(c_i+1)(a_1+1)...(a_n+1)fi=(ci+1)(a1+1)...(an+1)
    i⋅ki\cdot kik的约数个数为fi⋅k=(ci⋅k+1)(a1+1)...(an+1)f_{i\cdot k}=(c_{i\cdot k}+1)(a_1+1)...(a_n+1)fik=(cik+1)(a1+1)...(an+1)
    因此fi⋅k=fici⋅k+1ci+1f_{i\cdot k} = f_i\dfrac{c_{i\cdot k}+1}{c_{i}+1}fik=fici+1cik+1

接下来考虑dddggg数组

11 void init()
12 {
13     f[1]= g[1]= 1;
14     for(int i = 2; i <= n; i++){
15         if(!a[i]) { 
16             b[m++] = i;
18             d[i] = 1, g[i] = i + 1;
19         }
20         for(int j = 0; j < m && b[j] * i <= n; j++) {
21             int k = b[j];
22             a[i * k] = 1;
23             if(i % k == 0) {
26                 d[i * k] = d[i];
27                 g[i * k] = g[i] * k + d[i];
28                 break;
29             }
30             else {
33                 d[i * k] = g[i];
34                 g[i * k]= g[i] * (k + 1);
35             }
36          }
37     }
38 }
  • iii为质数时,di=1d_i = 1di=1gi=i+1g_i = i+1gi=i+1
  • 如果iii不是kkk的倍数,那么di⋅k=gid_{i\cdot k} = g_idik=gigi⋅k=(k+1)gig_{i\cdot k} =(k+1)g_igik=(k+1)gi
  • 如果iiikkk的倍数,那么di⋅k=did_{i\cdot k} = d_idik=digi⋅k=k⋅gi+dig_{i\cdot k} = k\cdot g_i+d_igik=kgi+di

上面都已经确定fif_ifi是根据分解质因数后的式子求约数个数,接下来大概率就要求约数和了(因为算术基本定理的知识点讲的就是这两点)
iii为质数时,i的约数和是i+1i+1i+1
iii不是kkk的倍数时,i⋅ki\cdot kik分解质因数后比iii多了一项kkk,在求约数和时,会多乘一项1+k1+k1+k
因此可以猜测gig_igi表示i的约数和。

gig_igi表示iii的约数和

  • iii为质数时,iii的约数和为i+1i+1i+1,有gi=i+1g_i = i+1gi=i+1
  • iii不是kkk的倍数时,kkki⋅ki\cdot kik的最小质因数
    i=p1a1...pnani = p_1^{a_1}...p_n^{a_n}i=p1a1...pnan,约数和为:gi=(1+p1+...+p1a1)...(1+pn+...+pnan)g_i=(1+p_1+...+p_1^{a_1})...(1+p_n+...+p_n^{a_n})gi=(1+p1+...+p1a1)...(1+pn+...+pnan)
    i⋅k=kp1a1...pnani\cdot k= kp_1^{a_1}...p_n^{a_n}ik=kp1a1...pnan,约数和为:gi⋅k=(1+k)(1+p1+...+p1a1)...(1+pn+...+pnan)g_{i\cdot k}=(1+k)(1+p_1+...+p_1^{a_1})...(1+p_n+...+p_n^{a_n})gik=(1+k)(1+p1+...+p1a1)...(1+pn+...+pnan)
    所以gi,k=(1+k)gig_{i,k}=(1+k)g_igi,k=(1+k)gi
  • iiikkk的倍数时,kkki⋅ki\cdot kik的最小质因数
    i=kcip1a1...pnani = k^{c_i}p_1^{a_1}...p_n^{a_n}i=kcip1a1...pnan,约数和gi=(1+k+...+kci)...(1+pn+...+pnan)g_i = (1+k+...+k^{c_i})...(1+p_n+...+p_n^{a_n})gi=(1+k+...+kci)...(1+pn+...+pnan)
    那么i⋅k=kci⋅kp1a1...pnani\cdot k= k^{c_{i\cdot k}}p_1^{a_1}...p_n^{a_n}ik=kcikp1a1...pnan,约数和gi⋅k=(1+k+...+kci,k)...(1+pn+...+pnan)g_{i\cdot k}= (1+k+...+k^{c_{i,k}})...(1+p_n+...+p_n^{a_n})gik=(1+k+...+kci,k)...(1+pn+...+pnan)
    由于此时ci,k=ci+1c_{i,k} = c_i+1ci,k=ci+1所以
    gi⋅k=(1+k+...+kci+1)...(1+pn+...+pnan)g_{i\cdot k}= (1+k+...+k^{c_{i}+1})...(1+p_n+...+p_n^{a_n})gik=(1+k+...+kci+1)...(1+pn+...+pnan)
    k⋅gi=(k+k2+...+kci+1)...(1+pn+...+pnan)k\cdot g_i = (k+k^2+...+k^{c_{i}+1})...(1+p_n+...+p_n^{a_n})kgi=(k+k2+...+kci+1)...(1+pn+...+pnan)
    gi⋅k=k⋅gi+(1+p1+...+p1a1)...(1+pn+...+pnan)g_{i\cdot k} = k\cdot g_i+(1+p_1+...+p_1^{a_1})...(1+p_n+...+p_n^{a_n})gik=kgi+(1+p1+...+p1a1)...(1+pn+...+pnan)
    已知:gi⋅k=k⋅gi+dig_{i\cdot k} = k\cdot g_i+d_igik=kgi+di
    因此可以认为di=(1+p1+...+p1a1)...(1+pn+...+pnan)d_i = (1+p_1+...+p_1^{a_1})...(1+p_n+...+p_n^{a_n})di=(1+p1+...+p1a1)...(1+pn+...+pnan),也就是iii除以自己最小质因数的幂kcik^{c_i}kci后剩下的数p1a1...pnanp_1^{a_1}...p_n^{a_n}p1a1...pnan的约数和。

did_idi表示iii分解质因数后,iii除以最小质因数的幂剩下的数的约数和
即如果i=p1a1p2a2...pnani = p_1^{a_1}p_2^{a_2}...p_n^{a_n}i=p1a1p2a2...pnan,那么did_idip2a2...pnanp_2^{a_2}...p_n^{a_n}p2a2...pnan的约数和。
di=(1+p2+...+p2a2)...(1+pn+...+pnan)d_i = (1+p_2+...+p_2^{a_2})...(1+p_n+...+p_n^{a_n})di=(1+p2+...+p2a2)...(1+pn+...+pnan)

  • iii为质数时,iii的最小质因数为iii,除以iii后剩下的数为1,约数和为1,因此di=1d_i=1di=1
  • iii不是kkk的倍数时,kkki⋅ki\cdot kik的最小质因数
    i⋅k=kp1a1...pnani\cdot k= kp_1^{a_1}...p_n^{a_n}ik=kp1a1...pnan,除以最小质因数的幂后,剩下的数为:p1a1...pnan=ip_1^{a_1}...p_n^{a_n} = ip1a1...pnan=i,该数的约数和为gig_igi,因此di⋅k=gid_{i\cdot k} = g_idik=gi
  • iiikkk的倍数时,kkki⋅ki\cdot kik的最小质因数
    i=kcip1a1...pnani = k^{c_i}p_1^{a_1}...p_n^{a_n}i=kcip1a1...pnan,除以最小质因数的幂kcik^{c_i}kci后,剩下的数为:p1a1...pnanp_1^{a_1}...p_n^{a_n}p1a1...pnan,其约数和为did_idi
    i⋅k=kci⋅kp1a1...pnani\cdot k = k^{c_{i\cdot k}}p_1^{a_1}...p_n^{a_n}ik=kcikp1a1...pnan,除以最小质因数的幂kci⋅kk^{c_{i\cdot k}}kcik后,剩下的数为:p1a1...pnanp_1^{a_1}...p_n^{a_n}p1a1...pnan,其约数和为di⋅kd_{i\cdot k}dik,也为did_idi
    因此di⋅k=did_{i\cdot k} = d_idik=di
判断题

1. 若输入不为1,把第13行删去不会影响输出的结果。( )
答:T
第13行语句为f[1]= g[1]= 1;
如果输入为1,输出f1f_1f1g1g_1g1,如果不进行初始化,f1,g1f_1,g_1f1g1的值为0,与其意义不相符。
如果输入不为1,即i不为1时,输出fi、gif_i、g_ifigi的值。
iii为质数时,fi=2f_i=2fi=2gi=i+1g_i=i+1gi=i+1,与f1f_1f1g1g_1g1无关。
iii为合数时,fif_ifigig_igi的求解过程依赖cic_icidid_idi,而cic_icidid_idi的值也和f1f_1f1g1g_1g1无关。
因此输入不为1时,f1,g1f_1,g_1f1,g1的值对fi,gif_i,g_ifi,gi的值没有影响。本题叙述正确。

2. 第25行的 f[i] / c[i * k]可能存在无法整除而向下取整的情况。 ( )
答:F
根据上述分析,此时ci⋅k=ci+1c_{i\cdot k}=c_i+1cik=ci+1
cic_iciiii的最小质因数是指数,设i=kcip1a1...pnani = k^{c_i}p_1^{a_1}...p_n^{a_n}i=kcip1a1...pnan,那么iii的约数个数fi=(ci+1)(a1+1)...(an+1)f_i = (c_i+1)(a_1+1)...(a_n+1)fi=(ci+1)(a1+1)...(an+1)fi/(ci+1)f_i/(c_i+1)fi/(ci+1)是一定可以整除的,该叙述错误。

3. 在执行完 init() 后,f 数组不是单调递增的,但 g 数组是单调递增的。 ( )
答:F
fif_ifiiii的约数个数,不是单调递增的,比如f4=3f_4 = 3f4=3f5=2f_5 = 2f5=2
gig_igiiii的约数和,也不是单调递增的,比如g4=1+2+4=7g_4 = 1+2+4=7g4=1+2+4=7g5=1+5=6g_5=1+5=6g5=1+5=6
因此叙述错误。

单选题

4. init 函数的时间复杂度为( )。
A. O(n)O(n)O(n)
B. O(nlog⁡n)O(n\log n)O(nlogn)
C. O(nn)O(n\sqrt{n})O(nn)
D. O(n2)O(n^2)O(n2)

答:A
代码整体是线性筛算法,线性筛的时间复杂度为O(n)O(n)O(n)

5. 在执行完 init() 后,f[1],f[2],f[3],…,f[100] 中有()个等于 2。
A. 23
B. 24
C. 25
D. 26

答:C
fi=2f_i=2fi=2表示iii只有2个约数。每个数都有约数:1与自己。只有两个约数的数为质数。那么本题问的是1∼1001\sim1001100中有多少个质数。
可以从小到大枚举质数,每行写10个:

2	3	5	7	11	13	17	19	23	29
31	37	41	43	47	53	59	61	67	71
73	79	83	89	97

或者在纸上用埃筛的方法。
100以内的数,偶数一定不是质数。写出剩下的奇数。

2 3 5 7 
11 13 15 17 19
21 23 25 27 29 
31 33 35 37 39
41 43 45 47 49
51 53 55 57 59
61 63 65 67 69
71 73 75 77 79
81 83 85 87 89
91 93 95 97 99 

去掉大于3的3的倍数,各位相加后是3的倍数的数是3的倍数。
去掉大于5的5的倍数,个位是5的都是5的倍数。
去掉7的倍数,有7∗7=497*7=4977=497∗11=777*11=77711=777∗13=917*13=91713=91
而后就可以结束了,因为如果n是合数,n一定存在因数小于等于n\sqrt{n}n,现在小于等于100=10\sqrt{100}=10100=10的质因数都看过了,那么剩下的数一定都是质数。

2 3 5 7 
11 13 17 19
23  29 
31 37
41 43 47
53  59
61 67
71 73 79
83 89
97 

1∼1001\sim1001100一共有25个质数。

6. 当输入为 1000 时,输出为()。
A. 15 1340
B. 15 2340
C. 16 2340
D. 16 1340

答:C
对1000进行分解质因数1000=23∗531000 = 2^3*5^31000=2353
1000的约数个数f1000=(1+3)(1+3)=16f_{1000} = (1+3)(1+3) = 16f1000=(1+3)(1+3)=16
1000的约数和为g1000=(1+2+22+23)(1+5+52+53)=(3+4+8)(6+25+125)=15∗156=2340g_{1000} = (1+2+2^2+2^3)(1+5+5^2+5^3) \\ = (3+4+8)(6+25+125)\\ =15*156=2340g1000=(1+2+22+23)(1+5+52+53)=(3+4+8)(6+25+125)=15156=2340
因此会输出161616234023402340,选C。

【答案】

  1. T
  2. F
  3. F
  4. A
  5. C
  6. C
http://www.dtcms.com/a/294103.html

相关文章:

  • Android组件化实现方案深度分析
  • Day 8-zhou R包批量安装小补充!!!
  • java设计模式 -【策略模式】
  • AJAX案例合集
  • flutter使用CupertinoPicker绘制一个传入数据源的省市区选择器
  • 二级建造师学习笔记-2025
  • 【Linux-云原生-笔记】keepalived相关
  • DenseNet详解,附模型代码(pytorch)
  • Python设计模式 - 桥接模式
  • vite搭建react-ts项目,@别名配置
  • Python-Pytorch编码习惯
  • Windows 编程辅助技能:速览定义
  • 生成式人工智能展望报告-欧盟-02-技术方面
  • 以 “有机” 重构增长:云集从电商平台到健康生活社区的跃迁
  • 突发限制下的破局之路:国产之光 Lynx 重构 AI 开发安全壁垒
  • Petalinux的常用指令
  • Hexo - 免费搭建个人博客04 - 创建另一个私人仓库,对Hexo项目进行版本管理
  • RabbitMQ--消费端单线程与多线程
  • 电子电气架构 --- 汽车软件全生命周期
  • 小架构step系列23:加载自定义配置
  • Vue 浏览器本地存储
  • 05-ES6
  • Linux修炼:进程概念(上)
  • apache-doris安装兼datax-web配置
  • 【2025】使用vue构建一个漂亮的天气卡片
  • 加载用户设置时遇到错误找到一个带有无效“icon“的配置文件。将该配置文件默认为无图标。确保设置“icon“时,该值是图像的有效文件路径“
  • 基于php的校园招聘平台
  • 三步实现Android系统级集成:预装Google TTS + 默认引擎设置 + 语音包预缓存方案
  • ArcGIS Pro从0开始制作中国主图及黄土高原地势区域图
  • opencv学习(图像处理)