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,把第13行删去不会影响输出的结果。( )
- 第25行的 f[i] / c[i * k]可能存在无法整除而向下取整的情况。 ( )
- 在执行完 init() 后,f 数组不是单调递增的,但 g 数组是单调递增的。 ( )
- 单选题
- init 函数的时间复杂度为( )。
A. O(n)O(n)O(n)
B. O(nlogn)O(n\log n)O(nlogn)
C. O(nn)O(n\sqrt{n})O(nn)
D. O(n2)O(n^2)O(n2) - 在执行完 init() 后,f[1],f[2],f[3],…,f[100] 中有()个等于 2。
A. 23
B. 24
C. 25
D. 26 - 当输入为 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=1n∑j=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个质数。
线性筛过程中,每个数都是通过其最小质因数被筛掉。k
是b[j]
,即第j个质数。确定i*k
是合数时,k
是i*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=1,fi=2f_i = 2fi=2;
- 如果iii不是kkk的倍数,那么ci⋅k=1c_{i\cdot k} = 1ci⋅k=1,fi⋅k=2fif_{i\cdot k} = 2f_ifi⋅k=2fi。
- 如果iii是kkk的倍数,那么ci⋅k=ci+1c_{i\cdot k} = c_i+1ci⋅k=ci+1,fi⋅k=fi(ci⋅k+1)ci⋅kf_{i\cdot k} = \dfrac{f_i(c_{i\cdot k}+1)}{c_{i\cdot k}}fi⋅k=ci⋅kfi(ci⋅k+1)。
由于fi⋅kf_{i\cdot k}fi⋅k的表达式中的ci⋅kc_{i\cdot k}ci⋅k的值就是刚刚赋值的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}fi⋅k=fici+1ci⋅k+1
到这里,如果想看出ccc数组与f数组的意义,必须对算术基本定理十分熟悉。(见上面【题目考点】)。
fi⋅kf_{i\cdot k}fi⋅k每次乘以ci⋅k+1c_{i\cdot k}+1ci⋅k+1,和求一个正数的约数个数过程中,每次乘以质因数的指数加1,即(an+1)(a_n+1)(an+1),很相似。进而想到ccc应该表示的是质因数的指数。
当iii是kkk的倍数时,kkk是i⋅ki\cdot ki⋅k的最小质因数,kkk也是iii的最小质因数。
那么i⋅ki\cdot ki⋅k分解质因数后kkk的指数比iii分解质因数后kkk的指数大1。
因此可以联想到:cic_ici表示iii分解质因数后,iii的最小质因数的指数。
cic_ici表示iii分解质因数后,iii的最小质因数的指数。
- 当iii为质数时,iii的最小质因数是自己,指数为1,因此ci=1c_i = 1ci=1
- 当iii不是kkk的倍数时,kkk是i⋅ki\cdot ki⋅k的最小质因数,kkk的指数为1,因此ci⋅k=1c_{i\cdot k} = 1ci⋅k=1
- 当iii是kkk的倍数时,kkk是i⋅ki\cdot ki⋅k与iii的最小质因数,i⋅ki\cdot ki⋅k分解质因数后kkk的指数比iii分解质因数后kkk的指数大1,因此有ci⋅k=ci+1c_{i\cdot k} = c_{i}+1ci⋅k=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}i⋅k=kci⋅kp1a1...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 ki⋅k的约数个数为(ci⋅k+1)(a1+1)...(an+1)(c_{i\cdot k}+1)(a_1+1)...(a_n+1)(ci⋅k+1)(a1+1)...(an+1)
iii的约数个数除以(ci+1)(c_i+1)(ci+1)再乘以(ci⋅k+1)(c_{i\cdot k}+1)(ci⋅k+1),就能得到i⋅ki\cdot ki⋅k的约数个数。
据此可知,fif_ifi表示iii的约数个数
fif_ifi表示iii的约数个数
- 当iii是质数时,iii的约数有2个,因此fi=2f_i=2fi=2。
- 当iii不是kkk的倍数时,kkk是i⋅ki\cdot ki⋅k的最小质因数
设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}i⋅k=kp1a1...pnan,约数个数为:fi⋅k=(1+1)(a1+1)...(an+1)f_{i\cdot k} = (1+1)(a_1+1)...(a_n+1)fi⋅k=(1+1)(a1+1)...(an+1)
因此fi⋅k=2fif_{i\cdot k} = 2f_ifi⋅k=2fi。 - 当iii是kkk的倍数时,kkk是i⋅ki\cdot ki⋅k的最小质因数
设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}i⋅k=kci⋅kp1a1...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 ki⋅k的约数个数为fi⋅k=(ci⋅k+1)(a1+1)...(an+1)f_{i\cdot k}=(c_{i\cdot k}+1)(a_1+1)...(a_n+1)fi⋅k=(ci⋅k+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}fi⋅k=fici+1ci⋅k+1
接下来考虑ddd与ggg数组
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=1,gi=i+1g_i = i+1gi=i+1;
- 如果iii不是kkk的倍数,那么di⋅k=gid_{i\cdot k} = g_idi⋅k=gi,gi⋅k=(k+1)gig_{i\cdot k} =(k+1)g_igi⋅k=(k+1)gi。
- 如果iii是kkk的倍数,那么di⋅k=did_{i\cdot k} = d_idi⋅k=di,gi⋅k=k⋅gi+dig_{i\cdot k} = k\cdot g_i+d_igi⋅k=k⋅gi+di。
上面都已经确定fif_ifi是根据分解质因数后的式子求约数个数,接下来大概率就要求约数和了(因为算术基本定理的知识点讲的就是这两点)
当iii为质数时,i的约数和是i+1i+1i+1
当iii不是kkk的倍数时,i⋅ki\cdot ki⋅k分解质因数后比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的倍数时,kkk是i⋅ki\cdot ki⋅k的最小质因数
设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}i⋅k=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})gi⋅k=(1+k)(1+p1+...+p1a1)...(1+pn+...+pnan)
所以gi,k=(1+k)gig_{i,k}=(1+k)g_igi,k=(1+k)gi - 当iii是kkk的倍数时,kkk是i⋅ki\cdot ki⋅k的最小质因数
设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}i⋅k=kci⋅kp1a1...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})gi⋅k=(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})gi⋅k=(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})k⋅gi=(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})gi⋅k=k⋅gi+(1+p1+...+p1a1)...(1+pn+...+pnan)
已知:gi⋅k=k⋅gi+dig_{i\cdot k} = k\cdot g_i+d_igi⋅k=k⋅gi+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_idi为p2a2...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的倍数时,kkk是i⋅ki\cdot ki⋅k的最小质因数
i⋅k=kp1a1...pnani\cdot k= kp_1^{a_1}...p_n^{a_n}i⋅k=kp1a1...pnan,除以最小质因数的幂后,剩下的数为:p1a1...pnan=ip_1^{a_1}...p_n^{a_n} = ip1a1...pnan=i,该数的约数和为gig_igi,因此di⋅k=gid_{i\cdot k} = g_idi⋅k=gi - 当iii是kkk的倍数时,kkk是i⋅ki\cdot ki⋅k的最小质因数
设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}i⋅k=kci⋅kp1a1...pnan,除以最小质因数的幂kci⋅kk^{c_{i\cdot k}}kci⋅k后,剩下的数为:p1a1...pnanp_1^{a_1}...p_n^{a_n}p1a1...pnan,其约数和为di⋅kd_{i\cdot k}di⋅k,也为did_idi
因此di⋅k=did_{i\cdot k} = d_idi⋅k=di
判断题
1. 若输入不为1,把第13行删去不会影响输出的结果。( )
答:T
第13行语句为f[1]= g[1]= 1;
如果输入为1,输出f1f_1f1,g1g_1g1,如果不进行初始化,f1,g1f_1,g_1f1,g1的值为0,与其意义不相符。
如果输入不为1,即i不为1时,输出fi、gif_i、g_ifi、gi的值。
当iii为质数时,fi=2f_i=2fi=2,gi=i+1g_i=i+1gi=i+1,与f1f_1f1与g1g_1g1无关。
当iii为合数时,fif_ifi,gig_igi的求解过程依赖cic_ici与did_idi,而cic_ici与did_idi的值也和f1f_1f1与g1g_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+1ci⋅k=ci+1,
cic_ici是iii的最小质因数是指数,设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_ifi是iii的约数个数,不是单调递增的,比如f4=3f_4 = 3f4=3,f5=2f_5 = 2f5=2
gig_igi是iii的约数和,也不是单调递增的,比如g4=1+2+4=7g_4 = 1+2+4=7g4=1+2+4=7,g5=1+5=6g_5=1+5=6g5=1+5=6
因此叙述错误。
单选题
4. init 函数的时间复杂度为( )。
A. O(n)O(n)O(n)
B. O(nlogn)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\sim1001∼100中有多少个质数。
可以从小到大枚举质数,每行写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=497∗7=49,7∗11=777*11=777∗11=77,7∗13=917*13=917∗13=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\sim1001∼100一共有25个质数。
6. 当输入为 1000 时,输出为()。
A. 15 1340
B. 15 2340
C. 16 2340
D. 16 1340
答:C
对1000进行分解质因数1000=23∗531000 = 2^3*5^31000=23∗53
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)=15∗156=2340
因此会输出161616与234023402340,选C。
【答案】
- T
- F
- F
- A
- C
- C