CSP-S 2025 提高级 第一轮(初赛) 阅读程序(3)

CSP-S 2025 提高级 第一轮(初赛) 阅读程序(3)

【题目】

cpp 复制代码
01 #include <algorithm>
02 #include <cstdio>
03 #include <cstring>
04 #include <vector>
05 #define ll long long
06 int n, m;
07 std::vector<int> k, p;
08 inline int mpow(int x, int k) {
09     int ans = 1;
10     for (; k; k = k >> 1, x = x * x) {
11         if (k & 1)
12             ans = ans * x;
13     }
14     return ans;
15 }
16 std::vector<int> ans1, ans2;
17 int cnt1, cnt2;
18 inline void dfs(std::vector<int>& ans, int& cnt, int l, int r, int v) {
19     if (l > r) {
20         ++cnt;
21         ans.push_back(v);
22         return;
23     }
24     for (int i = 1; i <= m; ++i) {
25         dfs(ans, cnt, l + 1, r, v + k[l] * mpow(i, p[l]));
26     }
27     return;
28 }
29 std::vector<int> cntans1;
30 int main() {
31     scanf("%d%d", &n, &m);
32     k.resize(n + 1);
33     p.resize(n + 1);
34     for (int i = 1; i <= n; ++i) {
35         scanf("%d%d", &k[i], &p[i]);
36     }
37     dfs(ans1, cnt1, 1, n >> 1, 0);
38     dfs(ans2, cnt2, (n >> 1) + 1, n, 0);
39     std::sort(ans1.begin(), ans1.end());
40     int newcnt1 = 1;
41     cntans1.push_back(1);
42     for (int i = 1; i < cnt1; ++i) {
43         if (ans1[i] == ans1[newcnt1 - 1]) {
44             ++cntans1[newcnt1 - 1];
45         } else {
46             ans1[newcnt1++] = ans1[i];
47             cntans1.push_back(1);
48         }
49     }
50     cnt1 = newcnt1;
51     std::sort(ans2.begin(), ans2.end());
52     int las = 0;
53     ll ans = 0;
54     for (int i = cnt2 - 1; i >= 0; --i) {
55         for (; las < cnt1 && ans1[las] + ans2[i] < 0; ++las)
56             ;
57         if (las < cnt1 && ans1[las] + ans2[i] == 0)
58             ans += cntans1[las];
59     }
60     printf("%lld\n", ans);
61     return 0;
62 }

判断题

  1. 删除第 515151 行的 "std::sort(ans2.begin(), ans2.end());" 后,代码输出的结果不会受到影响。

    A. 正确

    B. 错误

  2. 假设计算过程中不发生溢出,函数 mpow(x,k)mpow(x,k)mpow(x,k) 的功能是求出 xkx^kxk 的取值。

    A. 正确

    B. 错误

  3. 代码中第 393939 行到第 505050 行的目的是为了将 ans1ans1ans1 数组进行"去重"操作。

    A. 正确

    B. 错误

单选题

  1. 当输入为 "3 15 1 2 −1 2 1 23\ 15\ 1\ 2\ -1\ 2\ 1\ 23 15 1 2 −1 2 1 2" 时,输出结果为( )

    A. 4

    B. 8

    C. 0

    D. 10

  2. 记程序结束前 ppp 数组元素的最大值为 PPP,则该代码的时间复杂度是()。

    A. O(n)O(n)O(n)

    B. O(mnlog⁡mn)O\left(m^n \log m^n\right)O(mnlogmn)

    C. O(mn/2log⁡mn/2)O\left(m^{n/2} \log m^{n/2}\right)O(mn/2logmn/2)

    D. O(mn/2(log⁡mn/2+log⁡P))O\left(m^{n/2}\left(\log m^{n/2} + \log P\right)\right)O(mn/2(logmn/2+logP))

  3. 本题所求出的是( )。

    A. 满足 a,b,c∈[1,m]a,b,c\in[1,m]a,b,c∈[1,m] 的整数方程 a3+b3=c3a^3+b^3=c^3a3+b3=c3 的解的数量

    B. 满足 a,b,c∈[1,m]a,b,c\in[1,m]a,b,c∈[1,m] 的整数方程 a2+b2=c2a^2+b^2=c^2a2+b2=c2 的解的数量

    C. 满足 xi∈[0,m]x_i\in[0,m]xi∈[0,m] 的整数方程 ∑i=1nki⋅xi p=0\sum_{i=1}^n k_i\cdot x_i^{\,p}=0∑i=1nki⋅xip=0 的解的数量

    D. 满足 xi∈[1,m]x_i\in[1,m]xi∈[1,m] 的整数方程 ∑i=1nki⋅xi p=0\sum_{i=1}^n k_i\cdot x_i^{\,p}=0∑i=1nki⋅xip=0 的解的数量

【题目考点】

1. 快速幂
2. STL vector
3. 去重
4. 双指针
5. 不定方程

【解题思路】

cpp 复制代码
30 int main() {
31     scanf("%d%d", &n, &m);
32     k.resize(n + 1);
33     p.resize(n + 1);
34     for (int i = 1; i <= n; ++i) {
35         scanf("%d%d", &k[i], &p[i]);
36     }

首先输入nnn,mmm,已知k、pk、pk、p是vector,通过resize函数将vector的长度都设为n+1n+1n+1,这样可以使用vector的下标范围为[0,n][0,n][0,n],而后输入k、pk、pk、p序列,下标范围[1,n][1,n][1,n]。

vector对应的数据结构为顺序表。

cpp 复制代码
08 inline int mpow(int x, int k) {
09     int ans = 1;
10     for (; k; k = k >> 1, x = x * x) {
11         if (k & 1)
12             ans = ans * x;
13     }
14     return ans;
15 }

先看mpow函数,传入x、kx、kx、k,结果变量ansansans初值为111。看for循环,循环进行的条件为kkk,也就是k≠0k\neq 0k=0,每次循环k= k>>1,也就是kkk变为k/2k/2k/2,以及xxx变为x2x^2x2。如果k & 1为真,即判断如果kkk为奇数,那么让ansansans变为ans⋅xans\cdot xans⋅x。观察该过程可知,该函数实现了快速幂算法,xxx为底数,kkk为指数,求xkx^kxk

每次循环指数kkk变为原来的一半,底数xxx变为自己的平方,如果指数kkk为奇数,则结果乘上一个当前的底数。即:

  • 如果kkk为偶数,则xk=(x2)k2x^k=(x^2)^{\frac{k}{2}}xk=(x2)2k,
  • 如果kkk为奇数,则xk=x⋅(x2)k−12x^k=x\cdot (x^2)^{\frac{k-1}{2}}xk=x⋅(x2)2k−1

每次循环指数变为原来的一半,循环次数为O(log⁡k)O(\log k)O(logk),即快速幂求xkx^kxk的时间复杂度为O(log⁡k)O(\log k)O(logk)。

cpp 复制代码
18 inline void dfs(std::vector<int>& ans, int& cnt, int l, int r, int v) {
19     if (l > r) {
20         ++cnt;
21         ans.push_back(v);
22         return;
23     }
24     for (int i = 1; i <= m; ++i) {
25         dfs(ans, cnt, l + 1, r, v + k[l] * mpow(i, p[l]));
26     }
27     return;
28 }

观察dfsdfsdfs的参数可知,第一个参数ansansans为顺序表的引用,似乎用于保存结果。第二个参数cntcntcnt是int类型的引用,是保存对什么的计数。函数的参数是引用,可能会将该引用作为函数调用处理得到的结果。

而后传入l,rl, rl,r,似乎是表示区间[l,r][l, r][l,r],最后一个参数vvv表示数值。

一般情况,iii从1循环到mmm,每次循环进行递归调用,ansansans和cntcntcnt都直接下传,lll参数变为l+1l+1l+1,rrr不变。mpow(i, p[l])是求ipli^{p_l}ipl,vvv参数变为v+kl∗iplv+k_l*i^{p_l}v+kl∗ipl,不断递归调用后vvv参数保存的是v+kl∗iplv+k_l*i^{p_l}v+kl∗ipl的加和。

每次递归调用dfsdfsdfs,会在[1,m][1,m][1,m]中选择一个iii的值,将kl∗iplk_l*i^{p_l}kl∗ipl加入求和变量vvv,而后lll增加1。直到发生l>rl>rl>r,进入递归出口。这时计数变量cntcntcnt增加1,将当前的加和vvv添加进ansansans这个顺序表。

该过程相当于对于每个j∈[l,r]j\in [l, r]j∈[l,r],都让加和增加kjxjpjk_jx_j^{p_j}kjxjpj,其中xjx_jxj可以取[1,m][1,m][1,m]中的任意值(就是代码中iii的值)。加和为∑j=lrkjxjpj\sum\limits_{j=l}^rk_jx_j^{p_j}j=l∑rkjxjpj,将jjj变为iii来写,即∑i=lrkixipi\sum\limits_{i=l}^rk_ix_i^{p_i}i=l∑rkixipi。

因此可知该函数求的是:

有序列xl,xl+1,...,xrx_l, x_{l+1}, ..., x_rxl,xl+1,...,xr, 其中每个元素的取值范围xj∈[1,m],j∈[l,r]x_j\in[1,m], j\in[l, r]xj∈[1,m],j∈[l,r]。xxx序列中每个元素取[1,m][1,m][1,m]中的某个值,得到一个xxx序列。对于每个xxx序列,求出∑j=lrkjxjpj\sum\limits_{j=l}^rk_jx_j^{p_j}j=l∑rkjxjpj作为一个结果,将该结果添加进ansansans序列。cntcntcnt为结果的个数,即ansansans序列中元素的个数。

cpp 复制代码
37     dfs(ans1, cnt1, 1, n >> 1, 0);
38     dfs(ans2, cnt2, (n >> 1) + 1, n, 0);

主函数中两次调用dfsdfsdfs

  • 第一次调用[l,r][l, r][l,r]区间为[1,⌊n2⌋][1, \lfloor \frac{n}{2}\rfloor][1,⌊2n⌋],求出的结果保存在顺序表ans1ans1ans1,结果数量为cnt1cnt1cnt1。
  • 第二次调用的[l,r][l, r][l,r]区间为[⌊n2⌋+1,n][\lfloor \frac{n}{2}\rfloor+1,n][⌊2n⌋+1,n],求出的结果保存在顺序表ans2ans2ans2,结果数量为cnt2cnt2cnt2。
cpp 复制代码
39     std::sort(ans1.begin(), ans1.end());
40     int newcnt1 = 1;
41     cntans1.push_back(1);
42     for (int i = 1; i < cnt1; ++i) {
43         if (ans1[i] == ans1[newcnt1 - 1]) {
44             ++cntans1[newcnt1 - 1];
45         } else {
46             ans1[newcnt1++] = ans1[i];
47             cntans1.push_back(1);
48         }
49     }
50     cnt1 = newcnt1;

对顺序表ans1ans1ans1进行升序排序。newcntnewcntnewcnt是新的什么的计数,初值为1。cntans1cntans1cntans1是另一个顺序表,开始时有一个元素1,似乎newcnt1newcnt1newcnt1是cntans1cntans1cntans1这个顺序表中的元素个数,而cntans1cntans1cntans1中元素似乎也是计数量。

这一段的作用是:去重,以及记录每个元素出现的个数的过程。首先将ans1ans1ans1排序后,相同的元素都已经相邻。

可以认为经过去重后得到一个新的序列ans1′ans1'ans1′,newcnt1newcnt1newcnt1是ans1′ans1'ans1′的长度,ans1′ans1'ans1′的下标范围为[0,newcnt1−1][0,newcnt1-1][0,newcnt1−1]。cntans1[i]cntans1[i]cntans1[i]表示的是原序列ans1ans1ans1序列中ans1′[i]ans1'[i]ans1′[i]这个值出现的个数,cntans1cntans1cntans1和ans1′ans1'ans1′的下标概念一致。

例:原序列ans1ans1ans1排序后为1,1,2,2,2,31,1,2,2,2,31,1,2,2,2,3,处理后各数组为

0 1 2 3 4 5
ans1′ans1'ans1′ 1 2 3
cntans1cntans1cntans1 2 3 1
ans1ans1ans1 1 2 3 2 2 3

ans1′[newcnt1−1]ans1'[newcnt1-1]ans1′[newcnt1−1]是ans1′ans1'ans1′最后一个元素

  • 如果当前访问到的元素ans1[i]ans1[i]ans1[i]与最后一个添加到ans1′ans1'ans1′的元素ans1′[newcnt1−1]ans1'[newcnt1-1]ans1′[newcnt1−1]相同,那么ans1′[newcnt1−1]ans1'[newcnt1-1]ans1′[newcnt1−1]的个数cntans1[newcnt1−1]cntans1[newcnt1-1]cntans1[newcnt1−1]增加1。
  • 如果当前访问到的元素ans1[i]ans1[i]ans1[i]与ans1′[newcnt1−1]ans1'[newcnt1-1]ans1′[newcnt1−1]不同,那么就将新元素添加到ans1′ans1'ans1′的末尾位置newcnt1newcnt1newcnt1,ans1′ans1'ans1′中的元素个数newcnt1newcnt1newcnt1增加1,这个新的元素ans1′[newcnt1−1]ans1'[newcnt1-1]ans1′[newcnt1−1]的当前出现的次数为111,所以应该设cntans1[newcnt1−1]=1cntans1[newcnt1-1] = 1cntans1[newcnt1−1]=1,实际操作上,是在cntans1cntans1cntans1这个顺序表末尾添加一个1,效果相同。

由于对序列去重后的元素数量始终小于等于原序列的元素数量,即每次运行到43行时,newcnt1newcnt1newcnt1表示ans1ans1ans1区间[0,i−1][0,i-1][0,i−1]中去重后的元素数量,一定有newcnt1≤i−1<inewcnt1\le i-1<inewcnt1≤i−1<i,所以基本修改ans1[newcnt1]ans1[newcnt1]ans1[newcnt1],也不会影响遍历过程。(形象地说,newcnt1newcnt1newcnt1始终追不上iii),因此可以进行原地去重 ,不再设ans1′ans1'ans1′,直接使用顺序表ans1ans1ans1的下标[0,newcnt1−1][0,newcnt1-1][0,newcnt1−1]保存ans1′ans1'ans1′序列中的元素。去重结束后,顺序表ans1ans1ans1保存的就是去重后的序列,最后再使用newcnt1newcnt1newcnt1覆盖cnt1cnt1cnt1,那么去重后的序列长度为cnt1cnt1cnt1。cntans1[i]cntans1[i]cntans1[i]表示的是原序列中ans1[i]ans1[i]ans1[i]这个值出现的个数。

cpp 复制代码
51     std::sort(ans2.begin(), ans2.end());
52     int las = 0;
53     ll ans = 0;
54     for (int i = cnt2 - 1; i >= 0; --i) {
55         for (; las < cnt1 && ans1[las] + ans2[i] < 0; ++las)
56             ;
57         if (las < cnt1 && ans1[las] + ans2[i] == 0)
58             ans += cntans1[las];
59     }
60     printf("%lld\n", ans);

而后对顺序表ans2ans2ans2进行排序。laslaslas变量应该对应lastlastlast这个单词,表示上一个。结果变量ansansans初值为000

以下是双序列双指针算法,laslaslas是ans1ans1ans1的下标(指针),小到大变化。iii是ans2ans2ans2的下标,从大到小变化。
las<cnt1las<cnt1las<cnt1是为了限制下一个表达式中的ans1[las]ans1[las]ans1[las]不会出现数组下标越界的情况。

只要ans1[las]+ans2[i]<0ans1[las]+ans2[i]<0ans1[las]+ans2[i]<0,就让ans1ans1ans1中的下标laslaslas增加,这样可以增加表达式ans1[las]+ans2[i]ans1[las]+ans2[i]ans1[las]+ans2[i]的值,直到ans1[las]+ans2[i]≥0ans1[las]+ans2[i]\ge 0ans1[las]+ans2[i]≥0时,跳出循环。

而后判断

  • 如果ans1[las]+ans2[i]=0ans1[las]+ans2[i] = 0ans1[las]+ans2[i]=0,那么结果变量ansansans增加cntans1[las]cntans1[las]cntans1[las]。ans1[las]ans1[las]ans1[las]表示去重前ans1ans1ans1序列中有cntans1[las]cntans1[las]cntans1[las]个数值ans1[las]ans1[las]ans1[las]。每个数值加上ans2[i]ans2[i]ans2[i]后都为0。因此ans1ans1ans1原序列中有几个数与ans2[i]ans2[i]ans2[i]加和为0,结果就增加几。
  • 如果ans1[las]+ans2[i]>0ans1[las]+ans2[i] > 0ans1[las]+ans2[i]>0,或ans1[las]+ans2[i]=0ans1[las]+ans2[i] = 0ans1[las]+ans2[i]=0时完成了计数,那么让iii减少1。使得ans2[i]ans2[i]ans2[i]取到更小的值,重复上述循环。

该双指针算法求的是ans1ans1ans1原序列与ans2ans2ans2序列中,两序列各取一个数加和为0的方案数。

该双指针算法可以统计到所有满足加和为0数对的数量。用jjj表示laslaslas。

已知ans1ans1ans1与ans2ans2ans2都是升序的。

双指针移动的规则为:如果ans1[j]+ans2[i]<0ans1[j]+ans2[i]< 0ans1[j]+ans2[i]<0,jjj增加。如果ans1[j]+ans2[i]≥0ans1[j]+ans2[i]\ge 0ans1[j]+ans2[i]≥0,iii减少。

假设存在一个数对ans1[x],ans2[y]ans1[x], ans2[y]ans1[x],ans2[y],满足ans1[x]+ans2[y]=0ans1[x]+ans2[y] = 0ans1[x]+ans2[y]=0。
jjj从小到大变化,iii从大到小变化。j=xj=xj=x与i=yi=yi=y二者一定由一个事件先发生。

如果先发生j=xj = xj=x,那么此时i>yi>yi>y,ans[j]+ans[i]>ans[x]+ans[y]=0ans[j]+ans[i] > ans[x]+ans[y]=0ans[j]+ans[i]>ans[x]+ans[y]=0,接下来iii会不断减小,直到i=yi=yi=y,取到数对ans1[x],ans2[y]ans1[x], ans2[y]ans1[x],ans2[y]。

如果先发i=yi = yi=y,那么此时j<xj<xj<x,ans[j]+ans[i]<ans[x]+ans[y]=0ans[j]+ans[i] < ans[x]+ans[y]=0ans[j]+ans[i]<ans[x]+ans[y]=0,接下来jjj会不断增大,直到j=xj=xj=x,取到数对ans1[x],ans2[y]ans1[x], ans2[y]ans1[x],ans2[y]。

因此,只要存在数对ans1[x],ans2[y]ans1[x], ans2[y]ans1[x],ans2[y],通过该双指针算法一定可以取到。

对ans1ans1ans1进行去重的目的是为了提高统计满足ans1[x]+ans2[y]=0ans1[x]+ans2[y] = 0ans1[x]+ans2[y]=0的数对(ans1[x],ans2[y])(ans1[x], ans2[y])(ans1[x],ans2[y])的数量。

已知原ans1ans1ans1中元素是∑i=1⌊n2⌋kixipi\sum\limits_{i=1}^{\lfloor \frac{n}{2} \rfloor}k_ix_i^{p_i}i=1∑⌊2n⌋kixipi的某一个取值,原ans1ans1ans1中保存了当xi∈[1,m]x_i\in[1,m]xi∈[1,m]时∑i=1⌊n2⌋kixipi\sum\limits_{i=1}^{\lfloor \frac{n}{2} \rfloor}k_ix_i^{p_i}i=1∑⌊2n⌋kixipi的所有可能取值。
ans2ans2ans2中元素是∑i=⌊n2⌋+1nkixipi\sum\limits_{i=\lfloor \frac{n}{2} \rfloor+1}^{n}k_ix_i^{p_i}i=⌊2n⌋+1∑nkixipi的某一个取值,ans2ans2ans2中保存了当xi∈[1,m]x_i\in[1,m]xi∈[1,m]时∑i=⌊n2⌋+1nkixipi\sum\limits_{i=\lfloor \frac{n}{2} \rfloor+1}^{n}k_ix_i^{p_i}i=⌊2n⌋+1∑nkixipi的所有可能取值。

因此ans1ans1ans1中的一个元素加上ans2ans2ans2中的一个元素ans1[x]+ans2[y]ans1[x]+ans2[y]ans1[x]+ans2[y]是∑i=1nkixipi\sum\limits_{i=1}^{n}k_ix_i^{p_i}i=1∑nkixipi的某一个取值。本题求满足ans1[x]+ans2[y]=0ans1[x]+ans2[y] = 0ans1[x]+ans2[y]=0的数对的数量,即能够使得∑i=1nkixipi\sum\limits_{i=1}^{n}k_ix_i^{p_i}i=1∑nkixipi的xxx的取值的方案的数量。

对于xxx序列x1,x2,...,xnx_1, x_2, ..., x_nx1,x2,...,xn的一组取值,如果能够使得∑i=1nkixipi=0\sum\limits_{i=1}^{n}k_ix_i^{p_i}=0i=1∑nkixipi=0,那么这一组取值就是以x1,x2,...,xnx_1, x_2, ..., x_nx1,x2,...,xn为未知数的方程∑i=1nkixipi=0\sum\limits_{i=1}^{n}k_ix_i^{p_i}=0i=1∑nkixipi=0的一组解。

因此该代码所求解的问题为:不定方程∑i=1nkixipi=0\sum\limits_{i=1}^{n}k_ix_i^{p_i}=0i=1∑nkixipi=0,xi∈[1,m]x_i\in[1,m]xi∈[1,m],求该方程有多少组解。

判断题

28. 删除第 515151 行的 "std::sort(ans2.begin(), ans2.end());" 后,代码输出的结果不会受到影响。
A. 正确
B. 错误

正确答案:B

根据解析可知,该双指针算法成立依赖ans2ans2ans2是升序序列。如果不对ans2ans2ans2排序,该双指针算法就不会成立。

该代码中使用双指针算法求ans1ans1ans1中元素和ans2ans2ans2中元素加和为0的数对的数量。

例如:如果ans2ans2ans2不进行升序排序,有如下例子:

下标 0 1
ans1ans1ans1 1 2
ans2ans2ans2 -1 -2

ans1ans1ans1的下标为jjj,初值为0。ans2ans2ans2的下标为iii,初值为111。

双指针移动的规则为:如果ans1[j]+ans2[i]<0ans1[j]+ans2[i]< 0ans1[j]+ans2[i]<0,jjj增加。如果ans1[j]+ans2[i]≥0ans1[j]+ans2[i]\ge 0ans1[j]+ans2[i]≥0,iii减少。

首先ans1[j]+ans2[i]=1−2=−1<0ans1[j]+ans2[i] = 1-2=-1<0ans1[j]+ans2[i]=1−2=−1<0,jjj增加变为111。

而后ans1[j]+ans2[i]=2−2=0ans1[j]+ans2[i] = 2-2=0ans1[j]+ans2[i]=2−2=0,找到1个数对,而后iii减少,变为000。
ans1[j]+ans2[i]=2−1=1>0ans1[j]+ans2[i] = 2-1=1>0ans1[j]+ans2[i]=2−1=1>0,iii减少,变为−1-1−1,循环结束。

一共只找到了一对加和为0的数。而ans1ans1ans1和ans2ans2ans2中实际存在2对加和为0的数(1,−1),(2,−2)(1,-1), (2, -2)(1,−1),(2,−2)

因此如果不对ans2ans2ans2排序,会影响结果。该题叙述错误。

29. 假设计算过程中不发生溢出,函数 mpow(x,k)mpow(x,k)mpow(x,k) 的功能是求出 xkx^kxk 的取值。
A. 正确
B. 错误

正确答案:A
mpow(x,k)mpow(x,k)mpow(x,k) 的是以快速幂算法求出 xkx^kxk的值。

30. 代码中第 393939 行到第 505050 行的目的是为了将 ans1ans1ans1 数组进行"去重"操作。
A. 正确
B. 错误

正确答案:A

见上文解析。

单选题

31. 当输入为 "3 15 1 2 −1 2 1 23\ 15\ 1\ 2\ -1\ 2\ 1\ 23 15 1 2 −1 2 1 2" 时,输出结果为( )
A. 4
B. 8
C. 0
D. 10

正确答案:B
n=3,m=15,k1=1,p1=2,k2=−1,p2=2,k3=1,p3=2n=3, m=15, k_1=1,p_1=2,k_2=-1,p_2=2,k_3=1,p_3=2n=3,m=15,k1=1,p1=2,k2=−1,p2=2,k3=1,p3=2。
xi∈[1,15]x_i\in [1,15]xi∈[1,15],问k1x1p1+k2x2p2+k3x3p3=0k_1x_1^{p_1}+k_2x_2^{p_2}+k_3x_3^{p_3} = 0k1x1p1+k2x2p2+k3x3p3=0的解的数量。

即x12−x22+x32=0x_1^2-x_2^2+x_3^2=0x12−x22+x32=0在[1,15][1,15][1,15]中的正整数解的数量。

该方程等价于x12+x32=x22x_1^2+x_3^2=x_2^2x12+x32=x22符合勾股定理。

已知[1,15][1,15][1,15]中的勾股数有3 4 53\ 4\ 53 4 5及其倍数 6 8 10, 9 12 15\ 6\ 8\ 10,\ 9\ 12\ 15 6 8 10, 9 12 15,还有5 12 135\ 12\ 135 12 13,共4组勾股数。

每组勾股数中,较小的两个数可以分配x1,x3x_1,x_3x1,x3,有2种分配方案,每组勾股数可以产生2组x1,x2,x3x_1,x_2,x_3x1,x2,x3的取值。因此x1,x2,x3x_1,x_2,x_3x1,x2,x3有8组取值满足x12+x32=x22x_1^2+x_3^2=x_2^2x12+x32=x22,本题选B。

具体为:

x1x_1x1 x2x_2x2 x3x_3x3
3 5 4
4 5 3
6 10 8
8 10 6
5 13 12
12 13 5
  1. 记程序结束前 ppp 数组元素的最大值为 PPP,则该代码的时间复杂度是()。
    A. O(n)O(n)O(n)
    B. O(mnlog⁡mn)O\left(m^n \log m^n\right)O(mnlogmn)
    C. O(mn/2log⁡mn/2)O\left(m^{n/2} \log m^{n/2}\right)O(mn/2logmn/2)
    D. O(mn/2(log⁡mn/2+log⁡P))O\left(m^{n/2}\left(\log m^{n/2} + \log P\right)\right)O(mn/2(logmn/2+logP))

正确答案:D

分析该算法的时间复杂度,首先输入数据O(n)O(n)O(n)

两次调用dfsdfsdfs,每次对长为n2\frac{n}{2}2n的[l,r][l,r][l,r]区间,生成长为n2\frac{n}{2}2n的序列,求出∑i=lrkixipi\sum\limits_{i=l}^rk_ix_i^{p_i}i=l∑rkixipi。会递归调用n2\frac{n}{2}2n层,每层内循环mmm次,进行mmm次递归调用,总调用次数为O(mn2)O(m^{\frac{n}{2}})O(m2n)次。每次调用都会进行一次快速幂计算,快速幂的指数最大为PPP,因此执行快速幂的时间复杂度为O(log⁡P)O(\log P)O(logP),所以一次dfsdfsdfs的时间复杂度为O(mn2log⁡P)O(m^{\frac{n}{2}}\log P)O(m2nlogP)
∑i=lrkixipi\sum\limits_{i=l}^rk_ix_i^{p_i}i=l∑rkixipi的每个未知数xxx有mmm种取值,区间[l,r][l,r][l,r]长度为n2\frac{n}{2}2n,即该表达式中xxx的个数为n2\frac{n}{2}2n。根据乘法原理,执行dfsdfsdfs产生∑i=lrkixipi\sum\limits_{i=l}^rk_ix_i^{p_i}i=l∑rkixipi结果的数量为mn2m^{\frac{n}{2}}m2n,ans1ans1ans1和ans2ans2ans2序列中的元素数量为mn2m^{\frac{n}{2}}m2n。

接下来会分别对长为mn2m^{\frac{n}{2}}m2n的序列ans1ans1ans1和ans2ans2ans2进行升序排序,使用sort进行排序,底层原理是快速排序,时间复杂度为O(mn2log⁡mn2)O(m^{\frac{n}{2}}\log m^{\frac{n}{2}})O(m2nlogm2n)。而后对长为mn2m^{\frac{n}{2}}m2n的序列进行去重,时间复杂度为O(mn2)O(m^{\frac{n}{2}})O(m2n)。去重后的序列长度仍为mn2m^{\frac{n}{2}}m2n量级。对两个长为mn2m^{\frac{n}{2}}m2n的序列使用双指针算法,时间复杂度也为O(mn2)O(m^{\frac{n}{2}})O(m2n)

因此,该算法的整体时间复杂度为:O(2mn2log⁡P+2mn2log⁡mn2+2mn2)=O(mn2log⁡P+mn2log⁡mn2)=O(mn2(log⁡P+log⁡mn2))O(2m^{\frac{n}{2}}\log P+2m^{\frac{n}{2}}\log m^{\frac{n}{2}}+2m^{\frac{n}{2}})=O(m^{\frac{n}{2}}\log P+m^{\frac{n}{2}}\log m^{\frac{n}{2}})=O(m^{\frac{n}{2}}(\log P+\log m^{\frac{n}{2}}))O(2m2nlogP+2m2nlogm2n+2m2n)=O(m2nlogP+m2nlogm2n)=O(m2n(logP+logm2n)),选D。

  1. 本题所求出的是( )。
    A. 满足 a,b,c∈[1,m]a,b,c\in[1,m]a,b,c∈[1,m] 的整数方程 a3+b3=c3a^3+b^3=c^3a3+b3=c3 的解的数量
    B. 满足 a,b,c∈[1,m]a,b,c\in[1,m]a,b,c∈[1,m] 的整数方程 a2+b2=c2a^2+b^2=c^2a2+b2=c2 的解的数量
    C. 满足 xi∈[0,m]x_i\in[0,m]xi∈[0,m] 的整数方程 ∑i=1nki⋅xi p=0\sum_{i=1}^n k_i\cdot x_i^{\,p}=0∑i=1nki⋅xip=0 的解的数量
    D. 满足 xi∈[1,m]x_i\in[1,m]xi∈[1,m] 的整数方程 ∑i=1nki⋅xi p=0\sum_{i=1}^n k_i\cdot x_i^{\,p}=0∑i=1nki⋅xip=0 的解的数量

正确答案:D

见上文解析。

相关推荐
玛卡巴卡ldf44 分钟前
【LeetCode 手撕算法】(栈)有效括号、最小栈、字符串解码、每日温度、柱状图最大矩形
java·数据结构·算法·leetcode·力扣
happyprince1 小时前
05-FlagEmbedding 评估模块详解
算法
wuweijianlove1 小时前
算法优化的多目标平衡与性能建模研究的技术7
算法
汉克老师1 小时前
GESP6级C++考试语法知识(三、图与树(三))
c++·中序遍历·bst·完全二叉树·二叉排序树·gesp6级·gesp六级
_深海凉_1 小时前
LeetCode热题100-两两交换链表中的节点
算法·leetcode·链表
啊罗罗1 小时前
windows下,c++的axv2+fma/avx-vnni加速计算demo
c++·windows·算法
qq_283720051 小时前
Embedding 调优实战技巧:从原理到落地,打造高精度向量检索
python·算法·词嵌入·调优
li星野1 小时前
滑动窗口五题通关:从最小覆盖子串到水果成篮(Python + C++)
c++·python·学习
Xpower 171 小时前
OpenClaw近一月版本更替讲解
人工智能·学习·算法